Greater than, less than

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: Greater than, less than

Post by Kasumi »

Yes. BEQ covers just one cmp result. It's also exclusive from BCC after a compare. So if you do beq before bcc, you're cutting out one value from a group that may not need that value cut out from it. With BCC first, you can sieve out a much larger range of values. With BEQ first, you lose two cycles on a much larger range of numbers, than if you do BCC first, where you add two cycles to just one case. (Zero.) Edit2: Well, you also add the two cycles to all the BCS cases, but if you need to perform separate logic on <, >, and = something's gotta lose, and BCS is not exclusive from BEQ after a compare. If you are doing all three, BCS is usually a better first branch.

You said in the other topic the goal is performance, and it's usually faster. (But it depends on what you're comparing. Sometimes 0 really is the most common case.)

Edited to be hopefully be better explained.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Greater than, less than

Post by rainwarrior »

The difference is about time, not functionality. If you don't care how long it takes, then it doesn't matter.

If you do care, you'll want to arrange your code so that the most common case takes the shortest/fastest branch.

(Edit: missed Kasumi's post so this is redundant.)
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Greater than, less than

Post by tokumaru »

One obvious optimization is to start the comparison from the most significant digit and move your way down, because that will allow you to find the result without having to look at all the digits. You keep comparing while the digits in both numbers are the same, but as soon as you find one that's different, you'll have your answer. For example, when comparing 1350 and 1510, comparing 1 and 1 will not give you an answer, but comparing 3 and 5 will, so there's no need to compare the test.

With this in mind, it may be a good idea to have a BNE after each comparison branch away to a location that will use BCC/BCS to tell which number is larger (regardless of the position of the digit). Like this:

Code: Select all

  lda num1+n
  cmp num2+n
  bne decide
  lda num1+n-1
  cmp num2+n-1
  bne decide
  (...)
  lda num1+0
  cmp num2+0
  bne decide
  ;num1 = num2
decide:
  bcs greater than
  ;num1 < num2
greaterthan:
  ;num1 > num2
The loop is unrolled for speed, so you don't have to update X or check for end conditions with such small arrays (surely you don't have not than 8 digits to compare?).
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: Greater than, less than

Post by DRW »

tokumaru wrote:One obvious optimization is to start the comparison from the most significant digit and move your way down, because that will allow you to find the result without having to look at all the digits.
Yes, I know. That's why I said: The array aspect doesn't really have much to do with my question, only the question about a single comparison.

That's the way I did it now:

Code: Select all

	LeftEqualsRight = 0
	LeftLessThanRight = 1
	LeftGreaterThanRight = 2
	
.segment "ZEROPAGE"

	_CompareArraysPointerLeft: .res 2
	.exportzp _CompareArraysPointerLeft
	_CompareArraysPointerRight: .res 2
	.exportzp _CompareArraysPointerRight
	Size: .res 1

.segment "CODE"

_CompareArrays:
.export _CompareArrays_

	STA Size

	LDY #0

@loop:

	LDA (_CompareArraysPointerLeft), Y
	CMP (_CompareArraysPointerRight), Y
	BCC @leftLessThanRight
	BEQ @leftEqualsRight

	LDA #LeftGreaterThanRight
	JMP @end

@leftLessThanRight:

	LDA #LeftLessThanRight
	JMP @end

@leftEqualsRight:

	INY
	CPY Size
	BNE @loop
	LDA #LeftEqualsRight

@end:

	LDX #0

	RTS
The pointers are set in the code in C before calling the function.

By the way, is there any way to do this without the Size variable?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Greater than, less than

Post by tepples »

If you're comparing two multi-byte numbers but not doing a 3-way comparison, if you're fine with just knowing whether a < b or b >= a, you can just do most of the subtraction and ignore the result except for the carry. This method doesn't waste bytes on intermediate branches or cycles on untaken branches.

Code: Select all

  lda b_lo
  cmp a_lo  ; Use CMP instead of SBC to avoid needing to SEC
  lda b_mid
  sbc a_mid  ; Use SBC instead of CMP to respect previous carry
  lda b_hi
  sbc a_hi
  bcc a_is_less
  ; here: A is greater or equal
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: Greater than, less than

Post by DRW »

tepples wrote:If you're comparing two multi-byte numbers but not doing a 3-way comparison
It's a general purpose function. Sometimes I need to check for equal, sometimes for less than, sometimes for greater than. So, the function needs to be able to handle each case.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Greater than, less than

Post by tokumaru »

I though you were shooting for speed? Your loop looks significantly slower than than the unrolled loop I posted... If you need to compare numbers of different sizes, you can just have multiple entry points (load Y with Size - 1 and JSR to the appropriate entry point):

Code: Select all

Compare5Digits:
  lda (_CompareArraysPointerLeft), y
  cmp (_CompareArraysPointerRight), y
  bne Decide
  dey
Compare4Digits:
  lda (_CompareArraysPointerLeft), y
  cmp (_CompareArraysPointerRight), y
  bne Decide
  dey
Compare3Digits:
  lda (_CompareArraysPointerLeft), y
  cmp (_CompareArraysPointerRight), y
  bne Decide
  dey
Compare2Digits:
  lda (_CompareArraysPointerLeft), y
  cmp (_CompareArraysPointerRight), y
  bne Decide
  dey
Compare1Digit:
  lda (_CompareArraysPointerLeft), y
  cmp (_CompareArraysPointerRight), y
  bne Decide
  lda #LeftEqualsRight
  rts
Decide:
  bcc +
  lda #LeftGreaterThanRight
  rts
+ lda #LeftLessThanRight
  rts
I know that coming from a high-level language your first instinct is to use a loop, handle different sizes using a parameter and a variable, and have only one return point, but now you have the power of assembly! If you really are going for speed, you should consider these kinds of optimizations.
Post Reply