Log in Problems?
New User? Sign Up!

Author Message
 Brutte
 Posted: Sep 22, 2010 - 11:12 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 I wonder why: QA: "com x" <=> "sub 0xFF,x" but: "com x" => SREG_C set, "sub 0xFF,x" => SREG_C cleared, Is this SREG_C being set necessary in some (16-bit) operations? This is an exception and does not stick to standard algebraic rules. What about: QB: "asr x" and SREG_V=SREG_N^SREG_C? Where is it used in algebraic operations? Signed division and rounding towards zero?

 wagnerlip
 Posted: Feb 10, 2012 - 09:31 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 COM X is the One's Complement of all X register bits. It is used to invert the operation and make it easier in certain cases. For example, adding decimal 6 to decimal 13, equals decimal 19 Code:    Ldi R20,0x0D  ; binary 0000 1101    Ldi R21,0x06  ; binary 0000 0110    Add R20,R21   ; result 0001 0011 But in signed calculations it is easy to subtract One's Complement (instead of ADD) and then it is needed to subtract the Carry as the round-around borrowing bit. Code:    Ldi R20,0x0D  ; binary 0000 1101    Ldi R21,0x06  ; binary 0000 0110    Com R21       ; binary 1111 1001 Cy=1    Sbc R20,R21   ; result 0001 0011 This is why the COM X forces the Cy bit to "one", this is an optimization to subtract one's complement. Another example: Adding signed negative 21 to signed 56, that is 56 + -21, is the same as Subtracting the one's complement of -21 to +56 without worrying with signs. Code:    Ldi R20,0x38 ; binary 0011 1000 (signed +56d)    Ldi R21,0xEB ; binary 1110 1011 (signed -21d)    Add R20,R21  ; result 0010 0011 (signed +35d)    ; it is the same as    Ldi R20,0x38 ; binary 0011 1000 (signed +56d)    Ldi R21,0xEB ; binary 1110 1011 (signed -21d)    Com R21      ; binary 0001 0100 (unsigned 20)    Sbc R20,R21  ; binary 0010 0011 (signed +35) Okay, now the real MacCoy Subtracting signed -519d from 3516d, result should be +4035 (0x0FC3) Code:    Ldi R20,low(3516)  ; binary 0000 1101 (0xBC)    Ldi R21,high(3516) ; binary 1011 1100 (0x0D)    Ldi R22,low(-519)  ; binary 1111 1001 (0xF9)    Ldi R23,high(-519) ; binary 1111 1101 (0xFD)    Sub R20,R22        ; result 1100 0011 (0xC3) Cy=1    Sbc R21,R23        ; result 0000 1111 (0x0F) Cy=1 The result is correct, 0x0FC3, but carry bit is on, and this will propagate a false negative number to the next byte to the left, in a multi byte signed operation. If the left byte is zero, this carry will turn the byte 0xFF, instead of the three byte result of 0x000FC3 (+4035d), it will be 0xFF0FC3 (-61501d). There is a simple way to do that, using one's complement. So, instead of SUBtracting -519d from +3516d, lets ADD the one's complement of -519d to +3516d. Signed -519d = 0xFDF9. Its one's complement is 0x0206, decimal 518d (wow, easy again): Code:    Ldi R20,low(3516)  ; binary 1011 1100 (0xBC)    Ldi R21,high(3516) ; binary 0000 1101 (0x0D)    Ldi R22,low(-519)  ; binary 1111 1001 (0xF9)    Ldi R23,high(-519) ; binary 1111 1101 (0xFD)    Com R22            ; binary 0000 0110 (0x06) cy=1    Com R23            ; binary 0000 0010 (0x02) cy=1    Adc R20,R22        ; binary 1100 0011 (0xC3) cy=0    Adc R21,R23        ; binary 0000 1111 (0x0F) cy=0 Result is 0x0FC3, decimal +4035... easy!!! since the carry is off, will not propagate, result is correct. About the ASR X that may generates flags SVC, for a signed positive number, or SN/C or VNC for negative number. I wrote that in the other thread http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=35701 but I didn't explore it deeper enough, it is possible be used to round up the half-bit or use the C or V flag to reconstruct a whole bit from two half-bits. I just wonder, since I am not that smart. Update Text: Z80 microprocessor has DAA instruction, that adjusts decimal results after a simple ADD. In all other processors and controlers we need to do it by hand. It is not a big deal, but if the routine should be used often it would be stealing lots of precious clock cycles. The same for 'signed operations', the use of V S and N flags make some confusion and smoke in the air in the codeland. May be new instructions such as ADDS, ADDCS, SUBS, SBCS and MULS could reduce the code overhead for some of us that need to deal with signed numbers. I really wonder how many code written in assembly, use signed numbers. I did in the past, when dealing with external bipolar ADCs. Wagner Lipnharski Orlando Florida

 js
 Posted: Feb 10, 2012 - 10:09 PM
 Joined: Mar 28, 2001 Posts: 22744 Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)
 Wagner you have been away for a while but may want to check the date of the posts you are replying to. Not that there is anything wrong with a nice explanation but it may no longer help the op. _________________John Samperi Ampertronics Pty. Ltd. www.ampertronics.com.au * Electronic Design * Custom Products * Contract Assembly

 sternst
 Posted: Feb 10, 2012 - 10:16 PM
 Joined: Jul 23, 2001 Posts: 2728 Location: Osnabrueck, Germany
 js wrote: Wagner you have been away for a while but may want to check the date of the posts you are replying to. Not that there is anything wrong with a nice explanation but it may no longer help the op. He was asked by the OP in a different thread to answer this old question. http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=922769#922769 _________________Stefan Ernst

 Brutte
 Posted: Feb 11, 2012 - 05:16 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Guys, please be patient. I am still processing So, giving my answer: "Com x" can be discussed on two levels in here: 1. Do I ever need "com x" == ["sub 0xFF,x" + "sec"] on AVRs? That is, "is there any algebraic operation which cannot be processed on AVRs with at least the same performance, but without this sec exception"? Because if there is no such operation, the exception from algebraic rule has no value. Same as you implemented op-code for ["add 0xFF,x" +"clc"] IMHO.. 2. Usefulness of "sub 0xff,x"+"sec" on a generic ALU. Wasn't there some low transistor core (like intel 4040, '51 or PIC) in the past, which did not implement some arithmetic op-codes (like "sub" or "subi") where the existence of "com x" was crucial? What I mean is that I understand it is cool to use SREG_C in signed arithmetics and have SREG_C cleared after subtracting two ints, if the returned value is ge 0. The problem is that I cannot see the usefulness of of this feature on AVRs as subtracting two ints can be made with "sub"+"sbc", which is half the size and double the speed of your "com com adc adc" example. Another words - your argument proves the usefulness of "com x" on (level 2) generic ALU. But does not prove this exception of ["sub 0xFF,x" + "sec"] is of any use on AVR. Quote: and this will propagate a false negative number to the next byte to the left, in a multi byte signed operation. Lets suppose I want to give a counter-example and want to perform y= inta-intb..-intz on AVR. What is the use of this SREG_C at the end, when SREG_C is never used in tests on signed arithmetics? Does it indicate some kind of an overflow/error? Your way: Code: com intbL com intbH adc intaL,intbL adc intaH,intbH brbs SREG_C,we_got_overflow_on_b_sub ... ... com intzL com intzH adc intaL,intzL adc intaH,intzH brbs SREG_C,we_got_overflow_on_z_sub ;subtraction of ints was correct?? Now how I think it is made on AVRs: Code: sub intaL,intbL sbc intaH,intbH brbs SREG_S,we_got_overflow_on_b_sub ... ... sub intaL,intzL sbc intaH,intzH brbs SREG_S,we_got_overflow_on_z_sub ;subtraction completed without overflows What do you think? Quote: It is used to invert the operation and make it easier in certain cases. To prove the usefulness of ["sub 0xFF,x"+"sec"] exception you need to give a (level 1) argument, not the (level 2) one. An algorithm which cannot be performed better(-Os or -O3) than with regular algebraic rules on AVRs would do.

 wagnerlip
 Posted: Feb 11, 2012 - 08:13 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Hey John, sometimes I just do it, mostly in other electronic forums: wake up some old discussion when I have some new answers. Mostly is just for the record, in case someone do a search about the subject. No worries mate... Brutte, I made a small research about the SVNC flags when using bit shift operations. That's neat, and I wasn't surprised. Based on that I complete the research for all other instructions that change SVNC flags. Probably I got less than 2% of the whole truth, but you are correct. When thinking about signed numbers, bit 7 (polarity and N flag) gets in the way. If one creates instructions to deal with signed numbers, it would work only on bits zero to six, everything would be done there. Except if shifting bits right, then, bit 7 becomes bit 6. So, when dealing with signed numbers and shifting bits to the LEFT, LSL, ADD, ADDC and ROL, the right way should be only to work with bits zero to six. Bit seven should be preserved and untouched. Of course it doesn't work like that, so it should be updated according to some results. Strange enough, ASR is the only signed isntruction in the instruction set. ASR shifts all bits right and keep bit 7 untouched. Being a signed instruction it will give us some hints. This makes sense, since dividing a negative number by two doesn't change polarity. But the SNVC flags are becoming more friendly after all this typing. I like to type, the low pace organizes my polack/rusky/italian mind, considering that it is possible... Z FLAG It continues with the assumption that Z bit should be a large eight input NOR gate, if all is zero, Z bit becomes one. Just by coincidence, signed zero is also all bits off, so Z bit continues valid for signed or unsigned results. C FLAG It is all the way wrong for LEFT bit Shift with signed values. There is no way to fix it, except paying attention to SVN flags to decide what to do. N FLAG Any bit shift instruction, except ASR messes with N flag, it is ridiculous. The value 0x40 (+64d) imediately turns 0x80 (-128d) after a single LSL or ROL. The value 0x84 (-124d) becomes 0x42 (+66d) after a LSR or ROR, but ASR does it right, becomes 0xC2 (-62d), since the shift happens and bit 7 (N) is untouched. Resulting in the following statements for all instructions and the SVNC flags. Wherever you find below, consider Ra and Rb the operands and R the result after the operation. FINAL STATEMENT FOR ALL SHIFTs, LSL, LSR, ASR, ROL, ROR: S = N ⊕ V V = N ⊕ C So, S = N ⊕ N ⊕ C removing N S = C When N = 1, V = /C, S = /V, so S = C When N = 0, V = C, S = V, so S = C Then, S will always be = C and V will be = C for positives and /C for negatives. FINAL STATEMENT FOR ADD and ADC: S = N ⊕ V V = RaN • RbN • /RN + /RaN • /RbN • RN S = N ⊕ ( RaN • RbN • /RN + /RaN • /RbN • RN FINAL STATEMENT FOR ADD and ADC: S = N ⊕ V V = RaN • RbN • /RN + /RaN • /RbN • RN FINAL STATEMENT FOR ADIW: S = N ⊕ V V = 1 if highest bit (15) was zero and turns on. As this is exactly the inverse of Carry, then V = /C FINAL STATEMENT FOR SBIW: S = N ⊕ V V = /R15 • Rc7 FINAL STATEMENT FOR AND, ANDI, EOR, OR, ORI, SBR, TST and COM: S = N ⊕ V V = always cleared So, S = N FINAL STATEMENT FOR CP, CPC, CPI, SUB, SBC, SBCI, : S = N ⊕ V V = RaN • /RbN • /RN + /RaN • RbN • RN FINAL STATEMENT FOR DEC: S = N ⊕ V V = 1 if R=0x7F (only bit 7 down) = decimal +127, and this is cause before the DEC register was 0x80, so, signed overflow. Then, if V = 1, then S = 1 because N = 0 FINAL STATEMENT FOR INC and NEG: Exactly the reverse for DEC. S = N ⊕ V V = 1 if R=0x80 (only bit 7 up) = decimal -128, and this is cause before the INC register was 0x7F, so, signed overflow. Then, if V = 1, then S = 0 because N = 1 Now that all flags are on the table, we could think about how a code could be setup to deal with them in math operations involving signed number, dealing with overflows (V) and signed flag (S). Can't do it tonight... the italian side of the brain is complaining that the rusky and the polack are fighting...

 wagnerlip
 Posted: Feb 11, 2012 - 09:29 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Brutte, nice observation. Then I exercised the following 16 bits operation with both ideas. Given: V1 = \$A595 (-23147d) V2 = \$5718 (+22296d) V3 = V1 - V2 your way: Code:    ldi  r16,\$95    ldi  r17,\$A5    ldi  r18,\$18    ldi  r19,\$57    sub  r16,r18          ; 0x95 - 0x18                          ; r16 = 0x7D  Flags HSV    sbc  r17,r19          ; 0xA5 - 0x57 - Cy                          ; r17 = 0x4E  Flags HSV    brbs sreg_s,overflow  ; jump executes due S=1 Result in registers is 0x4E7D, but I wonder, the S flag only says that number shall be negative, but didn't fit in 16 bits, it overflows, so the V flag is on. If necessary a third byte to the left must exist and decremented from zero, making the result negative 0xFF4E7D (-45443d). It must be done testing S and V. my way Code:    ldi  r16,\$95    ldi  r17,\$A5    ldi  r18,\$18    ldi  r19,\$57    com  r18              ; r18 = 0xE7    com  r19              ; r19 = 0xA8    adc  r16,r18          ; 0x95 + 0xE7                          ; r16 = 0x7D  Flags SVC    adc  r17,r19          ; 0xA5 + 0xA8                          ; r17 = 0x4E  Flags SVC    brbs sreg_c,overflow  ; jump executes due Cy flag Here the result is exactly the same, 0x4E7D, but with Cy flag set, I just use it in the SBC to the left (3rd byte) and make it 0xFF, so the result becomes negative as 0xFF4E7D. At least in this exercise I did not need to test S or V. May be I need to do it in other cases? This is my first try, and used only Carry flag to solve it. Thoughts? Wagner Lipnharski Orlando Florida

 wagnerlip
 Posted: Feb 11, 2012 - 10:09 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Okay, exercised three 16 bits. Your way Code: V1 = 0x5718 V2 = 0x7143 V3 = 0x7143 V4 = V1 - V2 - V3        5718  sub   7143      ------          D5  Flags SNC        E5    Flags SNC   sbc  7143      ------          92  Flags SN        74    Flags SV S=1 means the number shall be negative V=1 means it doesn't fit in 16 bits and should be 0xFF7492 You need to observe both S and V to decide. V to know it overflows, and then S to make the 3rd left byte 0xFF. my way Code: V1 = 0x5718 V2 = 0x7143 V3 = 0x7143 V4 = V1 - V2 - V3  com V2 = 8EBC              5718  adc   8EBC      ------          D5  Flags HSN        E5    Flags HSN  adc   8EBC      ------          92  Flags HSNC        74    Flags HSVC S=1 means it shall be negative V=1 overflows, doesn't fit in 16 bits C=1 will be use in SBC to make left byte negative Result 0xFF7492 Again, I just need to use Carry flag. See, Cy flag is set whenever the ADC overflows the MSB byte, and when it happens, the value is automatically negative. Need to do some more exercises. Thoughts? Wagner Lipnharski orlando Florida

 Brutte
 Posted: Feb 11, 2012 - 01:13 PM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 I like the discussion but we are approaching the outer edge of the main subject, that is, what is the use of "com"==["sub 0xFF,x"+"sec"] on AVRs. Quote: I just use it in the SBC to the left (3rd byte) and make it 0xFF,(...)This is my first try, and used only Carry flag to solve it. Your argument is not enough, because: ["com" "com" "adc" "adc" + "sbc"] is still slower than ["sub" "sbc" + "sbc"] which uses same count of registers, returns a valid int24_t and is only a 3 word operation. Quote: Okay, exercised three 16 bits. ? Isn't a int16_t -int16_t enough for you to give a valid example? Quote: Need to do some more exercises.Thoughts? Provide a (level 1) "com" exception usage example.

 SprinterSB
 Posted: Feb 11, 2012 - 07:42 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Looking at code from avr-gcc, there is a single place where that compiler uses COM in the mentioned way, i.e. uses C=1 after COM: Negate a 3- or 4-byte value. For example, the sequence to negate a 3-byte value R is: Code: ;; R[0..2] := -R[0..2] performed as -R = (~R) + 1 com R[2] com R[1] com R[0] adc R[0], __zero_reg__ adc R[1], __zero_reg__ adc R[2], __zero_reg__ Where R[0] stands for the low byte of R and R[2] for its high byte. The sequence is used if the compiler cannot get hold of an upper register. As far as subtractions and additions are concerned, there is no difference between signed and unsigned additions (apart from the overflow flags, of course). Thus, COM does not help with subtractions or additions, be they signed or unsigned: If the values are in registers already, just use SUB/SBC resp. ADD/ADC sequences until all bytes are consumed. If the subtrahend/summand is a constant, then just subtract it with SUBI/SBCI. In the case of an addition, subtract the 2s complement.

 Brutte
 Posted: Feb 12, 2012 - 07:29 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 SprinterSB wrote: The sequence is used if the compiler cannot get hold of an upper register. This is an argument in some cases of discussion on level 2, but not on level 1, as AVRs have "neg x" == "sub 0x00,x" command (although lack "negc x"=="sbc 0x00,x"). The "neg x" sticks to algebraic rules and does not need hacks, exceptions or other tweaks of instruction set: Code: neg R[0] adc R[1],zero neg R[1] adc R[2],zero neg R[2] Not mentioning it is faster and smaller than "com adc".

 wagnerlip
 Posted: Feb 12, 2012 - 07:44 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Brutte wrote: I like the discussion but we are approaching the outer edge of the main subject, that is, what is the use of "com"==["sub 0xFF,x"+"sec"] on AVRs. Okay, the most common use is to change sign of a negative number. See an example below, in the signed 16 bits division. Code:                                     ;*************************************************************************** ;* ;* "div16s" - 16/16 Bit Signed Division ;* ;* This subroutine divides two 16 bit signed numbers ;* "dd16sH:dd16sL" (dividend) and "dv16sH:dv16sL" (divisor). ;* The result is placed in "dres16sH:dres16sL" and the remainder in ;* "drem16sH:drem16sL". ;*  ;* Number of words      :45 ;* Number of cycles     :252/268 (Min/Max) ;* Low registers used   :3 (d16s,drem16sL,drem16sH) ;* High registers used  :7 (dres16sL/dd16sL,dres16sH/dd16sH,dv16sL,dv16sH, ;*                                     ;***************************************************************************                                     ;***** Subroutine Register Variables                                     .def   d16s     =r13      ;sign register .def   drem16sL =r14      ;remainder low byte       .def   drem16sH =r15      ;remainder high byte .def   dres16sL =r16      ;result low byte .def   dres16sH =r17      ;result high byte .def   dd16sL   =r16      ;dividend low byte .def   dd16sH   =r17      ;dividend high byte .def   dv16sL   =r18      ;divisor low byte .def   dv16sH   =r19      ;divisor high byte .def   dcnt16s  =r20      ;loop counter                                 ;***** Code                                     div16s:   mov     d16s,dd16sH         ;move dividend High to sign register           eor     d16s,dv16sH         ;xor divisor High with sign register                                                   sbrs    dd16sH,7            ;if MSB in dividend set           rjmp    d16s_1           com     dd16sH              ;    change sign of dividend           com     dd16sL                 subi    dd16sL,low(-1)           sbci    dd16sL,high(-1) d16s_1:   sbrs    dv16sH,7            ;if MSB in divisor set           rjmp    d16s_2           com     dv16sH              ;    change sign of divisor           com     dv16sL                 subi    dv16sL,low(-1)           sbci    dv16sL,high(-1) d16s_2:   clr     drem16sL            ;clear remainder Low byte           sub     drem16sH,drem16sH   ;clear remainder High byte and carry           ldi     dcnt16s,17          ;init loop counter                                     d16s_3:   rol     dd16sL              ;shift left dividend           rol     dd16sH           dec     dcnt16s             ;decrement counter           brne    d16s_5              ;if done           sbrs    d16s,7              ;    if MSB in sign register set           rjmp    d16s_4           com     dres16sH            ;        change sign of result           com     dres16sL           subi    dres16sL,low(-1)           sbci    dres16sH,high(-1)                                     d16s_4: ret                         ;    return d16s_5:   rol     drem16sL            ;shift dividend into remainder           rol     drem16sH           sub     drem16sL,dv16sL     ;remainder = remainder - divisor           sbc     drem16sH,dv16sH     ;           brcc    d16s_6              ;if result negative           add     drem16sL,dv16sL     ;    restore remainder           adc     drem16sH,dv16sH           clc                         ;    clear carry to be shifted into result           rjmp    d16s_3              ;else d16s_6:   sec                         ;    set carry to be shifted into result           rjmp    d16s_3 For consideration about the use of the +Cy in the COM instruction (it may be your answer): Code:    ; To change the sign of a 16 bits negative:    ldi   r21, high(nnn)    ldi   r20, low(nnn)    COM   R20            ; same as 0xFF - R20 + Cy=1    COM   R21            ; same as 0xFF - R21 + Cy=1    SBCI  R21,0xFF       ; same as R21 + 1 - Cy (in case it was zero)    ; the same as    ldi   r21, high(nnn) ; val = 0x2005    ldi   r20, low(nnn)    SER   R22    SER   R23    SUB   R22,R20    SBC   R23,R21        ; res = 0xDFFA I like better to use this one: Code:    COM   SLow               COM   SHigh               SBCI  SHigh,0xFF    ADC   DLow,SLow   ; ADC instead of ADD save one instruction    ADC   DHigh,SHigh Then the used in the "div16s routine" above Code:    COM   SHigh    COM   SLow    SUBI  SLow,0xFF   ; doesn't need, if using ADC as above    SBCI  SHigh,0xFF         ADD   DLow,Slow    ADC   DHigh,SHigh Interesting discoveries while testing: Code:    ; NEG always set H=1, except when lower nibble is zero.    ; Here's a neat way to test lower nibble for zero.    NEG   R*    NEG   R*    BRHC  Low_Nibble_Z    ; is the same as the following, that needs extra register Rb for mask.    LDI   Rb,0x0F    AND   Rb,R*    BREQ  Low_Nibble_Z    ;----------------------    SER   R16    SUB   R16,R*    ; Cy=0    ; is the same as    SER   R16    EOR   R*,R16    ; Cy=0    ; also the same as    COM   R*        ; Cy=1 After double NEG R*, Z=1 if both nibbles are zero, H=0 if lower nibble is zero. I can think of few uses for it in BCD routines.

 Brutte
 Posted: Feb 12, 2012 - 09:29 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 wagnerlip wrote: See an example below I watch.. Code: com     dd16sL      subi    dd16sL,low(-1) ... com     dv16sL      subi    dv16sL,low(-1) ... com     dres16sL subi    dres16sL,low(-1) but cannot see a damn thing in it. Please enlighten me, where the SREG_C from "com" is used in it? Quote: ; To change the sign of a 16 bits negative: Where in this code you negate an int16_t? What about LSB? Quote: I like better to use this one: It needs 5 op-codes, 5 clocks and requires a high register.. I only do not get what this code is supposed to do Would you please comment your code? wagnerlip wrote: Okay, the most common use is Either my English is bad or you didn't get the idea of this topic. As I wrote earlier: Brutte wrote: What I mean is that I understand it is cool to use SREG_C in signed arithmetics I really appreciate our discussion but you are pushing it further and further into its border. Why are you giving me the most common usages of "com"? Is it supposed to be an argument in our discussion? Like "com was present in 57% of programs on AVRs, so it has to be useful with that SREG_C"? It is not an argument for me. Quote: NEG R* NEG R* what about cpi x,0x01? Come on! This is a discussion about "com"! Leave "neg" alone, even if you like it. Quote: I can think of few uses for it in BCD routines. Please don't.

 SprinterSB
 Posted: Feb 12, 2012 - 12:12 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Brutte wrote: SprinterSB wrote: The sequence is used if the compiler cannot get hold of an upper register. The "neg x" sticks to algebraic rules and does not need hacks, exceptions or other tweaks of instruction set: Code: neg R[0] adc R[1],zero neg R[1] adc R[2],zero neg R[2] Not mentioning it is faster and smaller than "com adc". The sequence is wrong. It does not propagate carry correctly. Just negate -1 = 0xffffff and with your code the result is 0x010001 instead of the correct 0x000001. You could have used Code: neg R[0] adc R[1],zero adc R[2],zero neg R[1] adc R[2],zero neg R[2] but that is not shorter than the clear and evident -R = 1 + ~R

 SprinterSB
 Posted: Feb 12, 2012 - 12:22 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 wagnerlip wrote: Code: com     dd16sH              ;    change sign of dividend com     dd16sL       subi    dd16sL,low(-1) sbci    dd16sL,high(-1) This is wrong, too. The 4th command must operate on the high byte. Similar for divisor. And even if you correct it: You can do smarter here with Code: com     dd16sH neg     dd16sL       sbci    dd16sH,-1

 Brutte
 Posted: Feb 12, 2012 - 01:59 PM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 SprinterSB wrote: The sequence is wrong. It does not propagate carry correctly. Yes, you are right, appending "adc neg" will not do the int24_t trick because of lack of SREG_C propagation on a third byte. Quote: but that is not shorter than the clear and evident -R = 1 + ~R Well, I would not call a "clear and evident" a valid argument in the discussion, but I suppose expanding 4*[com adc] for an int32_t would prove the usefulness of [sub 0xFF,x +sec]! Quote: You can do smarter here with "neg" "adc" "neg" (for int16_t) does not need an upper register.

 wagnerlip
 Posted: Feb 13, 2012 - 06:05 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Wagner wrote: Code:    ; NEG always set H=1, except when lower nibble is zero.    ; Here's a neat way to test lower nibble for zero.    NEG   R*    NEG   R*    BRHC  Low_Nibble_Z    ; is the same as the following, that needs extra register Rb for mask.    LDI   Rb,0x0F    AND   Rb,R*    BREQ  Low_Nibble_Z Brutte wrote: NEG R* NEG R* what about cpi x,0x01? Come on! This is a discussion about "com"! Leave "neg" alone, even if you like it. Yes Brutte! what about CPI x,0x01??? perhaps you would like to educate us all about how one could test the lower nibble for zero, using the instruction CPI x,0x01. I am fully curious now. ___________________ Wagner Lipnharski Orlando Florida USA

 Brutte
 Posted: Feb 13, 2012 - 06:17 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Quote: Yes Brutte! what about CPI x,0x01???(...)I am fully curious now. Are you curious about it in "Exceptions in SREG_x rules" topic? There is nothing exceptional in cpi - standard algebraic rules apply. _________________No RSTDISBL, no fun!

 wagnerlip
 Posted: Feb 13, 2012 - 06:34 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Yeah, yeah, yeah, I know, but, come on! it is not that difficult, explain CPI x,0x01 testing lower nibble for zero... 10 seconds of your time. Everyone will learn one more trick in AVR assembly. ___________________ Wagner Lipnharski Orlando Florida USA

 Brutte
 Posted: Feb 13, 2012 - 07:08 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Ok, ok "cpi x,0x01" or "cp x,0x01" or "cpc x,zero +SREG_C" compares/substitutes a "1" from x, so iff lower nibble of x was 0x0, a SREG_H is set(half-carry). Actually it is cpi x,0x?1 as we are not interested in higher nibble in this case. Is that correct? _________________No RSTDISBL, no fun!

 wagnerlip
 Posted: Feb 13, 2012 - 07:56 AM
 Joined: Nov 05, 2006 Posts: 37 Location: Orlando Florida USA
 Correct, it does, but as always with prejudice. CPI only works in upper registers. CP needs an extra register. Good if keeping a usual 0x01 reg onboard. NEG + NEG can work with any single register, but at expense of an extra code word and clock cycle.

 SprinterSB
 Posted: Feb 21, 2012 - 06:19 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Brutte wrote: "neg" "adc" "neg" (for int16_t) does not need an upper register. Thanks for pointing that out. It's just another micro-optimization to avr-gcc now: http://gcc.gnu.org/viewcvs/trunk/gcc/co ... rev=184447

 Brutte
 Posted: Feb 22, 2012 - 11:51 AM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 SprinterSB wrote: It's just another micro-optimization to avr-gcc now: You mean it was added into GCC? Great! Unfortunately I do not understand these patches.. Cannot find "adc" op-code there. You mean: Quote: neg B neg A sbc B,zero ? Looks ok. I am not sure about that but does GCC mind the SREG_S flag is not set correctly (when you neg 0x8000 )? SREG_Z propagates fine, it is set iff int==0.

 Bingo600
 Posted: Feb 22, 2012 - 01:47 PM
 Joined: Apr 25, 2004 Posts: 3942 Location: Denmark
 Brutte wrote: Unfortunately I do not understand these patches.. Cannot find "adc" op-code there. Brutte I don't understand the "inner works of avr-gcc" nor am i an assembler expert. But for a quick "patch" tour .... The lines starting with - are removed from the source, the lines starting with + are added. Maybe that gives a bit of a hint , to what SB has done (with the source) . /Bingo

 clawson
 Posted: Feb 22, 2012 - 04:04 PM
 Joined: Jul 18, 2005 Posts: 71689 Location: (using avr-gcc in) Finchingfield, Essex, England
 More to the point is that if you follow on from Sprinter's link you get to see the whole avr.md: http://gcc.gnu.org/viewcvs/trunk/gcc/co ... iew=markup It appears to be a work of genius in something resembling Forth a bit. The .md = machine description and it's presumably how the actual AVR architecture is known by GCC's code generator. Ultimately it looks like the _insn (instructions) it defines lead to the insertion of small snippets of AVr source code in the .s file. _________________

 Brutte
 Posted: Feb 22, 2012 - 04:14 PM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Bingo600 wrote: The lines starting with - are removed from the source, the lines starting with + are added. Thanks, there is an option to "colour" what was added and what was removed but there is no "adc" in what was added! So I guess SB must have made some further tweaks with -int and changed "neg adc neg" into "neg neg sbc" (which is ok). I suspect GCC is not very clever and ignores SREG's arguments and returned values and thus SREG_S and SREG_Z is of no use with GCC anyway. Quote: (set_attr "cc" "set_czn") ?? _________________No RSTDISBL, no fun!

 Bingo600
 Posted: Feb 22, 2012 - 07:00 PM
 Joined: Apr 25, 2004 Posts: 3942 Location: Denmark
 Brutte wrote: Quote: (set_attr "cc" "set_czn") ?? I really have no idea , but a guess would be that gcc needs to know what flags are affected/set by the things just executed. set C + Z + N flag or ?? /Bingo

 SprinterSB
 Posted: Feb 22, 2012 - 09:10 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Brutte wrote: Unfortunately I do not understand these patches... There is also colored diff which is more readable. It's not a patch actually but a diff between two SVN versions of GCC. Quote: Cannot find "adc" op-code there. You mean? Quote: neg B neg A sbc B, zero Yes, I used that sequence because it sets Z flag in a usable way: SBC propagates Z from NEG wheras ADC would clobber condition code. Quote: I am not sure about that but does GCC mind the SREG_S flag is not set correctly (when you neg 0x8000)? SREG_Z propagates fine, it is set iff int==0. avr-gcc's representation of AVR machine status is somewhat limitet and is not modelled 1:1. Condition code is not modelled explicitely but only implicitly by insn attribute "cc" that can have several states:none: not affected by insn, for example LDI, SWAP clobber: insn left cc in a mess (worst case), for example CALL compare: after a comparison (best case) set_zn: Z and N are set in a usable way, i.e. represent the output operand of the last insn set_czn: same as set_zn, but some other flags clobbered. Currently same effect like set_zn. set_n: N is set in a usable way. Currently not taken advantage of, same effect like clobber clawson wrote: The .md = machine description and it's presumably how the actual AVR architecture is known by GCC's code generator. Ultimately it looks like the _insn (instructions) it defines lead to the insertion of small snippets of AVR source code in the .s file. Insns (pronounced "insns" in order not to confuse them with instructions) are pieces of one of GCC's intermediate representations (IRs): RTL (Register Transfer Language) with lisp-like syntax. The insn's assembler template is very much like assembler template you know from inline assembler. And the constraints for the operands are very much like the operand constraints you know from inline assembler, except that GCC allows multiple alternatives (actually, inline asm allows multiple alternatives, too, but they are not very helpful because inline asm does not allow multiple asm templates). The insn pattern describes the insn's action on algebraic level, for neghi2 (\$0 = -\$1) from above it is Code: (set (match_operand:HI 0 "register_operand")      (neg:HI (match_operand:HI 1 "register_operand"))) HI stands for "Half Integer" which is a 16 bit scalar integer. This means "take a HI register \$1 as input, negate it, and set HI register \$0 to the result of the operation". Brutte wrote: I suspect GCC is not very clever and ignores SREG's arguments Consider Code: void bar (int); void neg (int x) {     x = -x;         if (x)         bar(x); } compiled -Os -dp -S and you get (older version without the change from above, i.e. insn neghi2 from the left side of the diff) Code: neg:    com r25    ;  6   neghi2/1   [length = 3]    neg r24    sbci r25,lo8(-1)    breq .L3    ;  8   branch   [length = 1]    rjmp bar    ;  11   call_insn/4   [length = 1] .L3:    ret    ;  22   return   [length = 1] Left is the filled-in insn template and in asm comments the insn name, constraint alternative used and length in words.

 Brutte
 Posted: Feb 22, 2012 - 11:35 PM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Quote: avr-gcc's representation of AVR machine status is somewhat limitet and is not modelled 1:1 But still impressive. Quote: Consider Ok, so I guess SREG_S is not a problem in above example as GCC does not understand SREG_V or SREG_S? Is there a chance that "com com adc adc" which sets SREG_S correctly could have any value over "neg neg sbc" in GCC? _________________No RSTDISBL, no fun!

 SprinterSB
 Posted: Feb 23, 2012 - 09:25 PM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Brutte wrote: I guess SREG_S is not a problem in above example as GCC does not understand SREG_V or SREG_S? It's not a problem. As said, avr-gcc does not model each bit of SREG. Instead, the compiler tracks how usable the contents of SREG are. That information is only used when a branch is emit. If the information is not there, avr-gcc emits a comparison. Otherwise, the comparision can be omitted. Other iformation that's used is if the value to be compared dies or not. For example, in Code: char c; void func_1 (long x) {     if (x < 1234567)         c = 0; } void func_14 (int x) {     if (x == -14)         c = 0; } x is unused after the comparison. Thus: Code: func_1:    cpi r22,-121    sbci r23,-42    sbci r24,18    cpc r25,__zero_reg__    brge .L1    sts c,__zero_reg__ .L1:    ret func_14:    adiw r24,14    brne .L3    sts c,__zero_reg__ .L3:    ret

 Brutte
 Posted: Feb 23, 2012 - 10:46 PM
 Joined: Oct 05, 2006 Posts: 3261 Location: Poland
 Quote: It's not a problem. Thanks for the detailed explanation. I can see GCC is quite advanced in the SREG subject. Code: brge .L1 I suppose this "brbx SREG_S,.." happens only after "compare: after a comparison (best case)" so it looks like even with "com com adc adc" (which does not propagate SREG_Z at all) that SREG_S is useless as there is no option in GCC: "SREG_S and SREG_N is ok, but the rest of the flags - not really as SREG_C inticates zero and SREG_Z tests for zero only on highest byte"..

 SprinterSB
 Posted: Feb 24, 2012 - 08:27 AM
 Joined: Dec 21, 2006 Posts: 1738 Location: Saar-Lor-Lux
 Brutte wrote: I can see GCC is quite advanced in the SREG subject. Not really... For example, the ave-gcc condition code model does not allow to model carry. This means that ADD/ADC sequences always have to be prnited as a block, i.e. as an insn with bunch of instructions as payload. It is not possible to write an insn for ADC, for example. Quote: I suppose this "brbx SREG_S,.." happens only after "compare" Basically, yes. Modelling SREG_S would not be am advantage at the moment. Moreover, the sequence as it is not is shorter. There is no way to make insn matching or constraint alternative selection depend on if condition code is used or unused after the insn.

 Display posts from previous:  All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 Jump to: Select a forum Forum index|--[AVR (8-bit) Technical Forums]|   |-- AVR forum|   |-- XMEGA forum|   |-- AVR Wireless forum|   |-- AVR GCC forum|   |-- AVR Studio 5 and Atmel Studio 6 forum|   |-- AVR studio 4 forum|   |-- AVRfreaks Academy forum|   |-- AVR Tutorials|--[AVR Software Framework]|   |-- AVR Software Framework|--[AVR32 (32-bit) Technical Forums]|   |-- AVR32 Linux Forum|   |-- AVR32 General (standalone)|   |-- AVR32 Software Tools|   |-- AVR32 Hardware|--[General Electronics Technical Forums]|   |-- General Electronics|   |-- Atmel Security Products|--[Non-technical forums]|   |-- AVRfreaks.net Housekeeping|--[Non-topical forums]|   |-- Off-topic forum|   |-- AVRfreaks Trading Post
All times are GMT + 1 Hour

 Powered by PNphpBB2 © 2003-2006 The PNphpBB GroupCredits