Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
Brutte
PostPosted: Sep 22, 2010 - 11:12 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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?
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 10, 2012 - 09:31 AM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
js
PostPosted: Feb 10, 2012 - 10:09 PM
10k+ Postman


Joined: Mar 28, 2001
Posts: 22625
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. Smile

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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
sternst
PostPosted: Feb 10, 2012 - 10:16 PM
Raving lunatic


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. Smile

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
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 11, 2012 - 05:16 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
Location: Poland

Guys, please be patient. I am still processing Smile

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.
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 11, 2012 - 08:13 AM
Rookie


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... Smile

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... Smile

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... Razz
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 11, 2012 - 09:29 AM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 11, 2012 - 10:09 AM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 11, 2012 - 01:13 PM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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.
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 11, 2012 - 07:42 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Brutte
PostPosted: Feb 12, 2012 - 07:29 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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".
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 12, 2012 - 07:44 AM
Rookie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 12, 2012 - 09:29 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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 Smile 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.
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 12, 2012 - 12:12 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
SprinterSB
PostPosted: Feb 12, 2012 - 12:22 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Brutte
PostPosted: Feb 12, 2012 - 01:59 PM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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.
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 13, 2012 - 06:05 AM
Rookie


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. Confused

___________________
Wagner Lipnharski
Orlando Florida USA
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 13, 2012 - 06:17 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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!
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 13, 2012 - 06:34 AM
Rookie


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
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 13, 2012 - 07:08 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
Location: Poland

Ok, ok Smile
"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!
 
 View user's profile Send private message  
Reply with quote Back to top
wagnerlip
PostPosted: Feb 13, 2012 - 07:56 AM
Rookie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 21, 2012 - 06:19 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Brutte
PostPosted: Feb 22, 2012 - 11:51 AM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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.
 
 View user's profile Send private message  
Reply with quote Back to top
Bingo600
PostPosted: Feb 22, 2012 - 01:47 PM
Raving lunatic


Joined: Apr 25, 2004
Posts: 3930
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) Smile .


/Bingo
 
 View user's profile Send private message  
Reply with quote Back to top
clawson
PostPosted: Feb 22, 2012 - 04:04 PM
10k+ Postman


Joined: Jul 18, 2005
Posts: 71208
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.

_________________
 
 View user's profile Send private message  
Reply with quote Back to top
Brutte
PostPosted: Feb 22, 2012 - 04:14 PM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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!
 
 View user's profile Send private message  
Reply with quote Back to top
Bingo600
PostPosted: Feb 22, 2012 - 07:00 PM
Raving lunatic


Joined: Apr 25, 2004
Posts: 3930
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
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 22, 2012 - 09:10 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Brutte
PostPosted: Feb 22, 2012 - 11:35 PM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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!
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 23, 2012 - 09:25 PM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Brutte
PostPosted: Feb 23, 2012 - 10:46 PM
Raving lunatic


Joined: Oct 05, 2006
Posts: 3239
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"..
 
 View user's profile Send private message  
Reply with quote Back to top
SprinterSB
PostPosted: Feb 24, 2012 - 08:27 AM
Posting Freak


Joined: Dec 21, 2006
Posts: 1733
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.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits