Shop OBEX P1 Docs P2 Docs Learn Events
SUBABS carry flag confusion — Parallax Forums

SUBABS carry flag confusion

ManAtWorkManAtWork Posts: 2,176
edited 2013-12-18 09:04 in Propeller 1
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
              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.
kopfkratz.gif
21 x 20 - 2K

Comments

  • ManAtWorkManAtWork Posts: 2,176
    edited 2013-12-17 02:13
    Ok, here's another solution
                  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.
  • AribaAriba Posts: 2,690
    edited 2013-12-17 06:18
    Here are two ideas:

    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
  • ManAtWorkManAtWork Posts: 2,176
    edited 2013-12-17 08:36
    Hi 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. :smile:

    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.
  • ManAtWorkManAtWork Posts: 2,176
    edited 2013-12-17 08:44
    One moment.... How about 2b)
             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).
  • AribaAriba Posts: 2,690
    edited 2013-12-17 11:05
    I don't think that 2b) will work.
    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 tested

    Andy
  • ManAtWorkManAtWork Posts: 2,176
    edited 2013-12-18 09:04
    Very clever solution, Andy! Until now I've only used _and_/_or_ conditions. But as conditions are encoded with 4 bits every combination has to be possible, also the ce_eq_z and c_ne_z conditions, of course.

    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...
Sign In or Register to comment.