Overflow detection

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Overflow detection
by on (#28220)
Which is the easiest way to detect overflow? I can imagine a construct of ifs, but this is quite inefficient. Is there a bitwise mask you can operate on the operands to predict the result? If so, how did you obtain it? I'm working on ADC and got everything (including the carry, which is just set to one if the result exceeds 0xFF) sorted except for the V-flag, which is giving me headaches.

Thanks for the help so far, it's appreciated :)

by on (#28223)
V logic for ADC:

set when: positive + positive = negative
or when: negative + negative = positive

therefore, you can check for the sum having mismatching signs as the two values being added.

Pseudo-code:

Code:
temp = A + val;
if( carry_set )
  temp += 1;

V_flag = (temp ^ A) & (temp ^ val) & 0x80;


here, V_flag will be nonzero (0x80) when V is to be set and 0 when it is to be cleared.


SBC works similarly, but logic is slightly different:

positive - negative = negative
negative - positive = positive

here, 'A' is the odd one out, not the final value. Therefore:

Code:
V_flag = (A ^ temp) & (A ^ val) & 0x80;

by on (#28227)
OK, thanks. Given immediate addressing, I now have (please bear with me):

Code:
case ADC:
  M=READMEM(rPC+1);
  R=M + rA + (fC ? 1 : 0);
  fV = (rA^R) & (M^R) & 0x80;
  rA = R&0x00FF; /* Copies the LSB of R into the accumulator */
  fC = R>>8; /* Stores MSB of result into carry */
  STEP(2,2); /* Macro that increases the PC and decreases the cycles */
  break;


Where:

R, M = 16-bits temp variables to store memory reads and results.
rA = accumulator register (#defined for readability)
rPC = program counter
fV = V-flag
fC = carry flag

So, is this any good? I suppose I can make a macro out of some of this, but this is just for verification. What I am kinda uncertain about, is whether moving a 16 bit integer into a single byte yields a predictable result, even when the MSB of the 16-bit integer is zero (as is the case in my operations).

by on (#28229)
Looks good to me. Only thing you're missing is setting N and Z according to the result.

johnnie wrote:
What I am kinda uncertain about, is whether moving a 16 bit integer into a single byte yields a predictable result, even when the MSB of the 16-bit integer is zero (as is the case in my operations).


To my knowledge, casting to an integer data type of smaller size truncates the value by removing the most significant bits. So the result would be predictable.

I never actually confirmed that -- but I'm pretty confident such behavior would be standardized in C/C++.

by on (#28231)
Talking about subtraction, my reference states that carry should be cleared "if overflow in bit 7". In other words, when my unsigned result > 255, I clear carry. However, it is nowhere said that carry should be set if this overflow did not occur. I have seen several cores (eg. FakeNES) doing just that by setting the carry storage byte to the MSB+1. This correctly clears the carry on overflow of bit 7, but implies setting carry when no overflow occurs. It appears to me that this is incorrect behaviour, or am I wrong?

by on (#28234)
Instructions that change flag states -- always change flag states (that is, they either set them or clear them -- they never leave them untouched). So SBC should be set when subtraction results in >= 0, and cleared otherwise. Note this applies only to flags that the instruction affects -- flags that it does not affect are never changed.

In short: FakeNES is doing it properly.

by on (#28257)
johnnie wrote:
What I am kinda uncertain about, is whether moving a 16 bit integer into a single byte yields a predictable result, even when the MSB of the 16-bit integer is zero (as is the case in my operations).

As long as the destination type is unsigned, it just truncates upper bits. If unsigned char is 8 bits on your machine (which it is on almost everything these days),

(unsigned char) foo

is equivalent to

(unsigned char) (foo & 0xFF)