SUBABS carry flag confusion

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
sar var,#31 wz,nr ' sign -> Z-flag mov old,var add var,corr if_z mins var,#0 ' avoid crossing zero if_nz maxs var,#0 sub corr,var ' subtract actual change from corr add corr,old
It's still 7 instructions long but it's somehow more "straightforward" and easier to understand.1) Limit the corr value first to the max possible delta:
neg tmp,var wc if_c maxs corr,tmp if_nc mins corr,tmp add var,corr
2) Make use of the Overflow flag that you get with ADDS:
adds var,corr wc if_c sub corr,var if_c sub var,var '= mov var,#0
Andy
1) is a good idea. However, you have to update corr at the end.
neg tmp,var wc mov limit,corr if_c maxs limit,tmp if_nc mins limit,tmp add var,limit ' add limited corr sub corr,limit ' remaining correction not added
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.
xor var,flipSign ' move border to 0 adds var,corr wc xor var,flipSign if_c sub corr,var if_c sub var,var '= mov var,#0
One instruction shorter but uses an additional long (flipSign = $80000000).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:
shr corr,#31 nr,wz 'get sign of corr to Z add var,corr wc 'Carry depends on 0-boundary mov corr,var if_c_ne_z mov corr,#0 'no overflow if_c_eq_z mov var,#0 'overflow var corr add -> C/Z var corr ------------------------------- -2 1 -1 0 1 -1 0 -2 3 1 1 1 0 1 5 -3 2 1 0 2 0 5 -7 -2 0 0 0 -2
I hope this works, it's not testedAndy
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...