how to detect overflow with SUMC?

The SUMC instruction is very useful if I want to add or subtract a value from a register based on the C flag. But how can I detect an over or underflow? I don't quite understand the stament "C = correct sign of (D +/- S)" in the docs.

What I like to do:
* if C is 0 then add S to D, set C if an overflow occurs (boundary $FFFFFFFF to $00000000 is crossed)
* if C is 1 then subtract S from D, set C if an underflow occurs (boundary $00000000 to $FFFFFFFF is crossed)

Is this possible with the SUMC instruction? Or do I have to split the operation into something like this?
  MODZ ... ' put C into Z
  if_z  SUB D,S wc
  if_nz ADD D,S wc
BTW, where are those constants defined that specify the actions for the MODC/Z instructions? Can't find them in the docs, again.

Comments

  • evanhevanh Posts: 10,230
    edited 2021-01-09 03:36
    ManAtWork wrote: »
    The SUMC instruction is very useful if I want to add or subtract a value from a register based on the C flag. But how can I detect an over or underflow? I don't quite understand the stament "C = correct sign of (D +/- S)" in the docs.
    I've forgotten exactly. I remember the discussion over naming of that but the name, "correct sign", no longer makes it clear for me either. TonyB put a lot of effort into ensuring the carry flag functioned better in the prop2.

    EDIT: Here's the original conversation - http://forums.parallax.com/discussion/167295/c-after-cmps-cmpsx-adds-addsx-subs-subsx/p1

    BTW, where are those constants defined that specify the actions for the MODC/Z instructions? Can't find them in the docs, again.
    The list is in instructions.txt in PNut zip, and now also added to end of main hardware google doc.

  • ManAtWork wrote: »
    What I like to do:
    * if C is 0 then add S to D, set C if an overflow occurs (boundary $FFFFFFFF to $00000000 is crossed)
    * if C is 1 then subtract S from D, set C if an underflow occurs (boundary $00000000 to $FFFFFFFF is crossed)

    This should be what you're looking for (untested):
    sumc d,s wc
    testb d,#31 xorc
    
  • evanhevanh Posts: 10,230
    Wuerfel_21 wrote: »
    This should be what you're looking for (untested):
    sumc d,s wc
    testb d,#31 xorc
    
    Looks right as per http://forums.parallax.com/discussion/comment/1421463/#Comment_1421463

  • Wuerfel_21 wrote: »
    This should be what you're looking for (untested):
    sumc d,s wc
    testb d,#31 xorc
    

    I've just tested it. The idea is good but the results are a bit different:
    * if C is 0 then add S to D, set C if a signed overflow occurs (boundary $7FFFFFFF to $80000000 is crossed)
    * if C is 1 then subtract S from D, set C if a signed underflow occurs (boundary $80000000 to $7FFFFFFF is crossed)

    However, that is not a problem for me. I can just add an offset to D. The exact number where the C bit is set doesn't really matter. It's only important that it happens once per "revolution" and at the same point when turning backwards.
  • evanhevanh Posts: 10,230
    Yeah, SUMxx instructions will be signed. You can tell because in the instruction sheet it says C = correct sign, where as for unsigned instructions it says C = carry.

  • TonyB_TonyB_ Posts: 1,600
    Just to add, "correct sign" means that even if there is an overflow and therefore the result is nonsense, C is always the the correct sign for any signed addition/subtraction, thus one can do a compare and not worry about a possible overflow. I came up with the term "correct sign" as it starts with the letter C.
  • An alternative that I think would actually trigger at the zero-crossing, but requires the Z flag (still untested):
    testb d,#31 wz
    sumc d,s wc
    ' now use if_diff/if_same conditions instead of if_c/if_nc
    
  • TonyB_TonyB_ Posts: 1,600
    Don't forget about TJV !
  • cgraceycgracey Posts: 13,180
    TonyB_ wrote: »
    Don't forget about TJV !

    Yes, TJV is what they're looking for.
  • AFAICS, TJV does test for the exact same condition as Wuerfels proposal.
    TJV Test D and jump to S** if D overflowed (D[31] != C, C = 'correct sign' from last addition/subtraction).
    I mean, "D[31]!=C" is the same as "testb d,#31 xorc".

    Thanks for the solution
  • TonyB_TonyB_ Posts: 1,600
    TJV is useful for more than testing overflow.
  • evanhevanh Posts: 10,230
    edited 2021-01-11 07:45
    I've dug up the old test code for this. It was very early stuff so needed some work to interface with my current setup. Here's a fresh set results with some extras added:
    00000004(reg1) + 00000005(reg2) = 00000009
    00000004(reg1) - 00000005(reg2) = ffffffff
    Collected flags = 0000028a
    ADD  reg1,reg2:  C = 0,  Z = 0
    SUB  reg1,reg2:  C = 1,  Z = 0
    CMP  reg1,reg2:  C = 1,  Z = 0
    ADDS reg1,reg2:  C = 0,  Z = 0
    SUBS reg1,reg2:  C = 1,  Z = 0
    CMPS reg1,reg2:  C = 1,  Z = 0
    
    00000005(reg1) + 00000004(reg2) = 00000009
    00000005(reg1) - 00000004(reg2) = 00000001
    Collected flags = 00000000
    ADD  reg1,reg2:  C = 0,  Z = 0
    SUB  reg1,reg2:  C = 0,  Z = 0
    CMP  reg1,reg2:  C = 0,  Z = 0
    ADDS reg1,reg2:  C = 0,  Z = 0
    SUBS reg1,reg2:  C = 0,  Z = 0
    CMPS reg1,reg2:  C = 0,  Z = 0
    
    80000000(reg1) + 80000000(reg2) = 00000000
    80000000(reg1) - 80000000(reg2) = 00000000
    Collected flags = 00000d75
    ADD  reg1,reg2:  C = 1,  Z = 1
    SUB  reg1,reg2:  C = 0,  Z = 1
    CMP  reg1,reg2:  C = 0,  Z = 1
    ADDS reg1,reg2:  C = 1,  Z = 1
    SUBS reg1,reg2:  C = 0,  Z = 1
    CMPS reg1,reg2:  C = 0,  Z = 1
    
    7fffffff(reg1) + 7fffffff(reg2) = fffffffe
    7fffffff(reg1) - 7fffffff(reg2) = 00000000
    Collected flags = 00000145
    ADD  reg1,reg2:  C = 0,  Z = 0
    SUB  reg1,reg2:  C = 0,  Z = 1
    CMP  reg1,reg2:  C = 0,  Z = 1
    ADDS reg1,reg2:  C = 0,  Z = 0
    SUBS reg1,reg2:  C = 0,  Z = 1
    CMPS reg1,reg2:  C = 0,  Z = 1
    
    7fffffff(reg1) + 80000000(reg2) = ffffffff
    7fffffff(reg1) - 80000000(reg2) = ffffffff
    Collected flags = 000002a0
    ADD  reg1,reg2:  C = 0,  Z = 0
    SUB  reg1,reg2:  C = 1,  Z = 0
    CMP  reg1,reg2:  C = 1,  Z = 0
    ADDS reg1,reg2:  C = 1,  Z = 0
    SUBS reg1,reg2:  C = 0,  Z = 0
    CMPS reg1,reg2:  C = 0,  Z = 0
    
    80000000(reg1) + 7fffffff(reg2) = ffffffff
    80000000(reg1) - 7fffffff(reg2) = 00000001
    Collected flags = 0000002a
    ADD  reg1,reg2:  C = 0,  Z = 0
    SUB  reg1,reg2:  C = 0,  Z = 0
    CMP  reg1,reg2:  C = 0,  Z = 0
    ADDS reg1,reg2:  C = 1,  Z = 0
    SUBS reg1,reg2:  C = 1,  Z = 0
    CMPS reg1,reg2:  C = 1,  Z = 0
    
    80000000(reg1) + 80000001(reg2) = 00000001
    80000000(reg1) - 80000001(reg2) = ffffffff
    Collected flags = 00000aaa
    ADD  reg1,reg2:  C = 1,  Z = 0
    SUB  reg1,reg2:  C = 1,  Z = 0
    CMP  reg1,reg2:  C = 1,  Z = 0
    ADDS reg1,reg2:  C = 1,  Z = 0
    SUBS reg1,reg2:  C = 1,  Z = 0
    CMPS reg1,reg2:  C = 1,  Z = 0
    
    80000001(reg1) + 80000000(reg2) = 00000001
    80000001(reg1) - 80000000(reg2) = 00000001
    Collected flags = 00000820
    ADD  reg1,reg2:  C = 1,  Z = 0
    SUB  reg1,reg2:  C = 0,  Z = 0
    CMP  reg1,reg2:  C = 0,  Z = 0
    ADDS reg1,reg2:  C = 1,  Z = 0
    SUBS reg1,reg2:  C = 0,  Z = 0
    CMPS reg1,reg2:  C = 0,  Z = 0
    
    7fffffff(reg1) + ffffffff(reg2) = 7ffffffe
    7fffffff(reg1) - ffffffff(reg2) = 80000000
    Collected flags = 00000a80
    ADD  reg1,reg2:  C = 1,  Z = 0
    SUB  reg1,reg2:  C = 1,  Z = 0
    CMP  reg1,reg2:  C = 1,  Z = 0
    ADDS reg1,reg2:  C = 0,  Z = 0
    SUBS reg1,reg2:  C = 0,  Z = 0
    CMPS reg1,reg2:  C = 0,  Z = 0
    
    ffffffff(reg1) + 7fffffff(reg2) = 7ffffffe
    ffffffff(reg1) - 7fffffff(reg2) = 80000000
    Collected flags = 0000080a
    ADD  reg1,reg2:  C = 1,  Z = 0
    SUB  reg1,reg2:  C = 0,  Z = 0
    CMP  reg1,reg2:  C = 0,  Z = 0
    ADDS reg1,reg2:  C = 0,  Z = 0
    SUBS reg1,reg2:  C = 1,  Z = 0
    CMPS reg1,reg2:  C = 1,  Z = 0
    
Sign In or Register to comment.