SUBABS carry flag confusion
ManAtWork
Posts: 2,176
I need to add a (signed) correction value to a (signed) variable with the restriction that this must not change the sign of the variable, or to be more precise, must not go beyond zero. If the correction (absolute) value is too large it has to be reduced so that the sum is zero and the remaining correction is remembered. My first idea to solve this was
However, this doesn't work as somebody else also found out here a long time ago. In the propeller manual the explanation of SUBABS says "C flag is set (1) if the subtraction resulted in an unsigned borrow (32-bit overflow)". But actually, there is no subtraction, i.e. the instruction does NOT do an ABS followed by a SUB, but instead does a conditional ADD (if source is negative).
The footnote (1) does reflect this ("If S is negative, C Result is the inverse of unsigned carry (for D + S)") but the two contradictional statements are misleading. If the source is negative, the C flag is not set to unsigned borrow for a SUB operation but instead to the inverse of carry for an ADD which is not the same. This should be documented more clearly.
So the problem is that in the code above the two lines starting with "if_z" and "if_nz" are meant to do something different but in reality do the same to the carry flag. A possible workaround could be
Or we could simply do an ADD and test if the sign changed
sar corr,#31 wz,nr ' sign into Z-flag (Z=1 means positive) if_z add var,corr wc ' corr>=0: C=carry if_nz subabs var,corr wc ' corr<0 : C=borrow (expected but wrong!) if_c neg corr,var ' if over/underflow then reduce abs(corr) if_c mov var,#0 ' and limit sum not to cross zero if_nc mov corr,#0 ' else clear corr
However, this doesn't work as somebody else also found out here a long time ago. In the propeller manual the explanation of SUBABS says "C flag is set (1) if the subtraction resulted in an unsigned borrow (32-bit overflow)". But actually, there is no subtraction, i.e. the instruction does NOT do an ABS followed by a SUB, but instead does a conditional ADD (if source is negative).
The footnote (1) does reflect this ("If S is negative, C Result is the inverse of unsigned carry (for D + S)") but the two contradictional statements are misleading. If the source is negative, the C flag is not set to unsigned borrow for a SUB operation but instead to the inverse of carry for an ADD which is not the same. This should be documented more clearly.
So the problem is that in the code above the two lines starting with "if_z" and "if_nz" are meant to do something different but in reality do the same to the carry flag. A possible workaround could be
sar corr,#31 wz,nr ' sign into Z-flag (Z=1 means positive) if_z add var,corr wc ' corr>=0: C=carry if_nz abs corr,corr if_nz sub var,corr wc ' corr<0 : C=borrow (really!) if_c ...
Or we could simply do an ADD and test if the sign changed
mov temp,var add var,corr xor temp,var shl temp,#1 wc if_c ...This uses the same number of instructions but needs an extra variable. It should work but somehow I feel that there must be a better solution than 7 instructions altogether.
Comments
1) Limit the corr value first to the max possible delta:
2) Make use of the Overflow flag that you get with ADDS:
Andy
1) is a good idea. However, you have to update corr at the end. This is still one instruction shorter.
2) doesn't work, I think. ADDS sets the carry when the destination crosses the $7FFFFFFF/$80000000 borderline. As corr is a small positive or negative value this will never happen. Or in other words, adding corr=2 to var=-1 should set the C-flag as it changes the sign of var but ADDS doesn't. Also, adding corr=-2 to var=1 should set the C-flag (sign changes) but ADDS doesn't.
I was reading your description wrong and tought you will just limit the correction value, but now I see you want remember the remaining correction if you can't do the full correction.
So here is a 5 instruction solution without any temporary variables: I hope this works, it's not tested
Andy
I have to admit that I found out that my specification is weak. If var is 0 the behaviour is unclear. Almost all µC define a sign change as the transition between -1 and 0 but mathematically 0 has no defined sign. I have to try out what happens...