Search |
 |
|
 |
| Author |
Message |
|
|
Posted: Sep 22, 2010 - 11:12 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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? |
|
|
| |
|
|
|
|
|
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 10, 2012 - 10:09 PM |
|


Joined: Mar 28, 2001
Posts: 20381
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
|
| |
|
|
|
|
|
Posted: Feb 10, 2012 - 10:16 PM |
|


Joined: Jul 23, 2001
Posts: 2438
Location: Osnabrueck, Germany
|
|
|
|
|
|
|
Posted: Feb 11, 2012 - 05:16 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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. |
|
|
| |
|
|
|
|
|
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...  |
|
|
| |
|
|
|
|
|
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 |
|
|
| |
|
|
|
|
|
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 11, 2012 - 01:13 PM |
|

Joined: Oct 05, 2006
Posts: 2248
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 11, 2012 - 07:42 PM |
|


Joined: Dec 21, 2006
Posts: 1483
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 12, 2012 - 07:29 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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". |
|
|
| |
|
|
|
|
|
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 12, 2012 - 09:29 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 12, 2012 - 12:12 PM |
|


Joined: Dec 21, 2006
Posts: 1483
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 12, 2012 - 12:22 PM |
|


Joined: Dec 21, 2006
Posts: 1483
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
|
|
|
| |
|
|
|
|
|
Posted: Feb 12, 2012 - 01:59 PM |
|

Joined: Oct 05, 2006
Posts: 2248
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. |
|
|
| |
|
|
|
|
|
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 13, 2012 - 06:17 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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!
|
| |
|
|
|
|
|
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 13, 2012 - 07:08 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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!
|
| |
|
|
|
|
|
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 21, 2012 - 06:19 PM |
|


Joined: Dec 21, 2006
Posts: 1483
Location: Saar-Lor-Lux
|
|
|
|
|
|
|
Posted: Feb 22, 2012 - 11:51 AM |
|

Joined: Oct 05, 2006
Posts: 2248
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 01:47 PM |
|


Joined: Apr 25, 2004
Posts: 3809
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 04:04 PM |
|


Joined: Jul 18, 2005
Posts: 62324
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. |
_________________
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 04:14 PM |
|

Joined: Oct 05, 2006
Posts: 2248
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!
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 07:00 PM |
|


Joined: Apr 25, 2004
Posts: 3809
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 |
|
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 09:10 PM |
|


Joined: Dec 21, 2006
Posts: 1483
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. |
|
|
| |
|
|
|
|
|
Posted: Feb 22, 2012 - 11:35 PM |
|

Joined: Oct 05, 2006
Posts: 2248
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!
|
| |
|
|
|
|
|
Posted: Feb 23, 2012 - 09:25 PM |
|


Joined: Dec 21, 2006
Posts: 1483
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
|
|
|
| |
|
|
|
|
|
Posted: Feb 23, 2012 - 10:46 PM |
|

Joined: Oct 05, 2006
Posts: 2248
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".. |
|
|
| |
|
|
|
|
|
Posted: Feb 24, 2012 - 08:27 AM |
|


Joined: Dec 21, 2006
Posts: 1483
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. |
|
|
| |
|
|
|
|
|
|
|
|