This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Testing for 8 bit unsigned wraparound.

Testing for 8 bit unsigned wraparound.
by on (#53617)
I've run into a situation where knowing if I've wrapped from $ff to $00 or $00 to $ff would be convenient. I need to know this in both directions; in other words regardless of whether I add a positive or negative 8 bit number to another 8 bit number.

It seems I can test for whether a negative OR a carry was generated; however this can happen passing from 127 to -128 and vice versa---BUT, since that generates an overflow flag, I can test that first, and if no overflow was generated then I can test for negative or carry. So I came up with this:

Code:

;some arithmetic I'm interested in testing for wraparound
  lda #$00
  clc
  adc #$ff

;the wraparound test
;if overflow is set, we passed the 127 to -128 boundary so we know ;wraparound did not occur
  bvs skipWraparoundLogic   
;get processor status
  php                       
  pla
;test for negative or carry bit at the same time
  and #$81
;if neither happened wraparound did not occur
  beq skipWraparoundLogic   
;here is where I do something because wraparound happened
skipWraparoundLogic:



Is there a simpler way to do this?

by on (#53618)
In that example adding #$FF to 0 won't wrap, it'll remain $FF with carry clear. Subtracting $FF would (that's one way to make a cheap "INC A" instruction :)).

It's pretty simple to test it in one direction, carry clear if it wrapped on SBC, and set if wrapped on ADC. It seems like these checks should be in every case where a wrap-around can occur. Is there a particular reason to have it separate and checking for 2 conditions at the same time?

If the code gets repetitive and/or strange looking, you could always make it into a macro. If the code you posted works, that's great because it's not many instructions.

by on (#53619)
For unsigned numbers you just have to test the carry flag :
When adding :
C = 0 -> No wraparound occurred
C = 1 ->Wraparound occurred
When subtracting :
C = 0 -> Wraparound occurred
C = 1 -> No wraparound occurred

You'll only have to deal with the V flag if you deal with signed numbers. Of course there is some cases where you don't know if the previous opperation was an adding or a substracting. This DID happen to me when coding sprite mazing logic when a sprite relative position could be either positive or negative, and I had to hide sprites whenever a wraparound occured (so that they don't appear on the wrong side of the screen). Then I had to check the sign of the relative position and implement the logic for both the "positive" and "negative" case.

by on (#53620)
Memblers:
But the value in A, will change from 0 to $ff. Negative will be set, which in this case tells me wraparound has occurred by adding a negative number to something. If I added 1 to $ff, then carry would be set, which would tell me wraparound occurred in the positive direction. That's what I'm looking for...just "did I pass from 0 to 255 or vice versa?"

*edit* Bregalad: It looks like I may have to do exactly what you said, testing the sign and using different logic for each case.

*edit* It looks like I may need to test whether a carry or a negative was generated exclusively:

Code:

;some arithmetic I'm interested in testing for wraparound
  lda #$00
  clc
  adc #$ff

;the wraparound test
;if overflow is set, we passed the 127 to -128 boundary so we know ;wraparound did not occur
  bvs skipWraparoundLogic   
;get processor status
  php                       
  pla
;test for negative or carry bit at the same time
  and #$81
;if neither happened wraparound did not occur
  beq skipWraparoundLogic   
  cmp #$81 ;make sure that both are not set at the same time.
  beq skipWraparoundLogic
;here is where I do something because wraparound happened
skipWraparoundLogic:



This will account for situations like adding -64 to 200.

It looks like this works for all the situations I'm interested in..I'll have to test it more to be sure.

by on (#53622)
If I understand the problem correctly....

You want to add a signed number to an unsigned number, and check to see if the unsigned number wrapped as a result.

IE:
0 + (-3) would wrap
245 + 23 would wrap
129 + (-4) would not wrap
etc

Am I understanding right?

I ran through all the scenarios and there's no way to check for this with the ADC flags alone. You'll need to examine the source numbers.

Here's what I came up with:
Code:
P = positlve
N = negative
c,v,n = flags set by ADC
- = no flags set by ADC
* = wrap you want to identify


P + P = P   -
P + P = N   nv
P + N = P   c
P + N = N   n    *
N + P = P   c    *
N + P = N   n
N + N = P   vc
N + N = N   nc


Only 2 of these instances cause a wrap, and neither of them have unique flag combinations. The only way you could do this would be to check the sign of the original numbers, and even then the code for it would be pretty large.

The way I see to do it is to check for 2 things:

- see if signed number's high bit matches sum's high bit
- see if original number's high bit doesn't match signed number's

If both of those are true, you have a wrap. Otherwise you don't.

Here's the code I came up with:

Code:
; o = original number
; s = signed number
; r = result (sum of addition)

lda r
eor s
bmi didnt_wrap  ; s and r bits mismatched, no wrap

lda r
eor o
bpl didnt_wrap  ; r and o bits matched, no wrap

; otherwise it wrapped

by on (#53624)
Disch,

I think there is a way to use the adc flags. I edited my previous post with a slight modification to the code. I've been testing this in the 6502 simulator but have so far failed to find a case where it does not work...am I missing something? *edit* yes, I was missing something. I finally found a case that breaks it. Namely, P + N = P, as you had indicated in your table.

by on (#53628)
N+P=N
and
P+N=P

Both of these will give you trouble.

by on (#53629)
Yes, I saw both cases in your table but had only tested P + N = P when I broke my original code. Thanks for the solution Disch! I think I should be able to apply this to my code.

by on (#53902)
Okay, turns out I just used the simple sign test/ carry test suggestion Bregalad made. It seemed a little more straightforward. Disch's solution is interesting though! I got caught up in wanting to find a ridiculously simple way to test for wraparound, but it looks like there's no way around using about 6 or so instructions, regardless of whether you test sign and then do sec/adc or clc/adc, and test the resulting carry, or if you use a solution such as Disch's.

I think I was solving a similar problem to what Bregalad described. My meta sprites have signed 8 bit sprite offsets, and I needed a way to know whether any individual sprite pops on or off of the screen when added to the original coordinate. Works really well! All 8 bit math, too.