A few remarks on the avr-gcc inline assembler

Go To Last Post
36 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the hope this will help at least a bit.

Similarly, Levenkay wrote:
As for resorting to inline assembler, I agree that would let me express the operation succinctly. The mechanisms that gcc's inline assembler requires you to use in order to reference the C variables, though, are more complex and (to me) obscure than the most bizzare C++ feature ever thought of being on its worst day. In the gcc docs, the "asm constraints" are explained by statements like, "When making an adjunctive covariant reference to an interstitially reflexive variable, prefix a treble-clef character to the intermediate term's constraint". I do use inline asm for cases where access to the carry or T flag(s) is key. But not being able to understand the documentation (which I suspect requires more knowledge about the compiler internals than I'm ever likely to have) makes each use fraught with worry. I try to be sparing. I have to remain in awe of folk like you who at least *seem* comfortable with it.

A few conclusions:

  1. in my opinion, in cases where tighter control over mcu resources (mainly execution time, but also memory) is required, assembler is to be deployed.

    There are two forms of it: inline or not.

  2. complete functions in assembler are preferred because of the issues described in following. These can be conveniently written in a "normal" syntax to a separate ".S" file and subsequently linked to the rest.

    The avr-gcc ABI is to be observed for the "rules" such as parameter passing, register usage etc.

    The function can be written first in C, then compiled only (using e.g. the -S command-line switch to avr-gcc), and the resulting assembler file can then be used as a template or starting point for modifications.

  3. Inline assembler is preferred for short snippets to be inlined to C functions, replacing possibly a poorly compiling portion, where the overhead of function call is untolerable, and/or an intensive usage of variables already present in registers is a bonus. In rare cases, inline assembler can be used to implement features unimplemented by the C compiler, e.g. Carlos Lamas' GET_FAR_ADDRESS() macro.

  4. Generally, there is not enough quality documentation on the inline assembler's details. Basic reading is the "cookbook", but it lacks a couple of details needed for the insight. The following text attempts to explain these bits and should be read in conjunction with the "cookbook" (ideally, parts of it be merged into it in the future).

    The reference source is supposed to be the relevant portions of gcc manual (or, better than the online version, the offline version installed alongside with the compiler at [WinAVR]/doc/gcc/HTML/gcc-[version]/gcc/Extended-Asm.htm and [same path]/Constraints.htm); however, that is notoriously poorly organized and sparse.

  5. The exact effect of "volatile" keyword in conjunction with "__asm__" keyword may be different from some expectations (namely it does not provide "code barrier", preventing reordering of code). It merely tells the compiler (optimizer) not to remove the code, even if it appears to do "nothing" (e.g. if it has no input/output operands (in which case the compiler sets the __asm__() statement implicitly volatile)) as it may have side effects (similarly to variables tagged volatile and (seemingly redundant) accesses to them). The compiler still can remove such code if it proves that it is never reachable.

    Similarly the effect of clobbers (including "memory") on code reordering. While these ensure correct ordering of variables accesses (and thus correct execution of the abstract machine as per the C language specification), it won't prevent reordering of apparently unrelated statements, which may have impact on timing (which is irrelevant for the language, but utmost important for many microcontroller applications). The canonical example is reordering of lengthy operations across __asm__("cli")/__asm__("sei"), resulting in interrupts being disabled significantly longer than intended (with detrimental effect on interrupts' latency).

    Although dramatic code reordering across __asm__() statements, or even reordering of __asm__() themselves, is rarely observed in the wild, the result of compilation shall be checked for correctness (the brave may also pester the high esteemed gcc developers for remedy).

  6. The inline assembler syntax is ugly, too verbose (i.e. a lot of "extra" characters (doublequotes, backslash-escaped sequences) to be typed in every line, in contrast to "normal" assembler) and hard to edit.

    For some recommendations to alleviate this problem (e.g. using $ as a line separator) see discussion below.

For better understanding of the inline assembler, the developer must understand all the steps of a C program translation. (It is informative to have a look at the results of all steps of translation of an inline-assembler piece, produced e.g. using the --save-temps command-line switch to avr-gcc).

  1. Preprocessing.

    Generally, the assembler snippet itself, i.e. the first "parameter" of the __asm__() statement, is a single string, with all the consequences of this fact:

    • The usual syntax for asm is to write each statement into a separate line. Strings cannot exceed a source line, so the following is incorrect:
      __asm__(
      "  ldi  r16, 3
         ldi  r17, 5 "
      );

      Fortunately, the compiler in the first steps of compilation concatenates strings with only whitespace between them (which includes end-of-line characters), so the following is correct (from the point of view of preprocessor and compiler, not assembler, see below):

      __asm__(
      "  ldi  r16, 3 "
      "  ldi  r17, 5 "
      );

      We now fast forward to the point where the compiler takes the string, strips the quote characters, concatenates it, replaces the operands (see below) and passes the processed code to assembler. This string above would result in the following output in .s file:

        ldi  r16, 3   ldi  r17, 5 

      But the avr-as assembler requires a single instruction on a line, so whatever goes after the "ldi r16, 3" sees as "garbage at end of line" (this is the expression used in the actual error message). As the compiler just before concatenation of strings also converts the escaped characters, we shall then insert escaped end-of-line characters to the strings, which results in the familiar ugly syntax (\n would be sufficient; \t is added for tidy look in the output listfile):

      __asm__(
      "  ldi  r16, 3 \n\t"
      "  ldi  r17, 5 \n\t"
      );

    • the preprocessor does not perform macro expansion within strings. This is why you can't write e.g.
      __asm__("in r16, PINB");

      Constants defined through macros must therefore be passed through operands/constraints (see below).

      Sometimes, if the number of asm parameters is exhausted, numeric constants can be stringified and then concatenated with the asm statements' string, e.g.

      #define STRINGIFY_(a) #a
      #define STRINGIFY(a) STRINGIFY_(a)
      #define THREE 3
      
        __asm__(
          "ldi  r20, " STRINGIFY(THREE) " \n\t"
          "ldi  r21, 5 \n\t"
        );

  2. There is also a gotcha in the form of IO register names (as explained above, these have to be used through the operands). These are defined with their "memory" address in mind, so if you intend to use them as IO (as parameter of instructions in, out, sbi/cbi, sbis/sbic), 0x20 has to be deduced, best through using a macro already defined for this purpose:

        __asm__(
          "sbic  %[_PIND], 6 \n\t"
          :
          : [_PIND]    "I"  (_SFR_IO_ADDR(PIND))
        );
    

    Inline assembler snippets are often defined as macros and intended for repeated use; this is sufficiently covered in the "cookbook". Maybe one gotcha: as macros have to be single-line and we are used to write multi-line asm, the backslash line-merging mechanism has to be employed in those macros, which in turn gets into way of comments.

  3. Compiler (after string de-escaping and concatenation) parses the string for % characters (and subsequent "tokens") and attempts to replace them for the respective operands; either through order or through name. This is described sufficiently in the "cookbook" together with their syntax.

    What should be discussed more is the meaning of "constraints" associated with the operands. Let's start with that the word "constraints" is misleading: while the compiler indeed performs certain "sanity checks" on the operands, they mainly prescribe how the compiler shall treat the operand before substituting it for the %-token. This will be dealt with in more detail below.

    Also, there is a table in the "cookbook" which seems to prescribe, which constrain is to be used with which instruction; this is completely misleading, as there are almost always multiple constraints leading to correct parameters for an instruction.

    The ugliness of the syntax of constraints can be partially alleviated by grouping them to a "vertical structure", e.g.

    __asm__(
        "[... some asm statements ...]"
        : [p]        "=&e" (__tmpptr)
        : [d]        "r"   ((uint8_t)(__data))
        , [offsetTx] "M"   (offsetof(TRs485Buffers, uartTx))
        , [mask]     "M"   (RS485_TX_BUFFER_SIZE - 1)
    );

    but of course that is upon one's individual preference.

    There is also a limit (16 or 32, I don't remember the exact number) on the number of operands in a single __asm__() statement, so in more complex snippets you can run to troubles; the first to give up in this situation are the constant input constraints (see in the following).

    • Let's start with "input" operands, as they are simpler. There are basically two types of constraints: "constants" and "registers".

      The first type results simply in a constant known at compile (or link) time, e.g. value of an expression, offset of a struct member and similar. Typical "constant" constraints are "M" and "I". "X" is also an interesting and useful constraint, meaning "any operand whatsoever ;-)".

      The second type of constraints instructs the compiler to supply a register (or a set of consecutive registers, see below) containing the *current value* of variable or variable expression. So, the compiler sets aside the required number of registers for the use in __asm__() statement, fills them with the value of expression, and replaces the respective "%-tokens" in the assembler string by the appropriate register names.

      The number of registers set aside is given by the effective type of the "assigned" variable or expression. The "higher" registers of the range are then accessed using the "%A", "%B" etc. mechanism as described in the cookbook.

      Typical "register" constraints are "r", "d", "w", "x" to "z".

    • output operands - these are to say to the compiler, "supply a register name (or register range) for the given variable, the __asm__() will fill those registers, and then store them to their respective variables". Thus, only "register" type of constraints are meaningful together with direct variable names (no expressions). There must also be an extra character just before the constraint letter: "+" if the same variable is to be read before and written after __asm__() (i.e. if its previous value is essential to the asm() construct), "&" if an unique register shall be used (the compiler has a nasty habit of assigning the same register for unrelated input and output operands unless explicitly told by this character not to do so), "=" otherwise.

    • the clobbers are described in the "cookbook" in sufficient detail

    Sometimes the algorithm in the inline asm snippet needs an extra register or two above those allocated by the "register" type of operands. While these can be explicitly named as "r16" etc. and then listed in the clobbers, this is a "bad thing" to do, as it prevents the compiler to chose an optimal register (e.g. unused at the moment by the surrounding code). The way how to do this "properly" is to define a local (best to do it as local as possible, i.e. create a block just around the __asm__() statement and define it there) C variable (never to be used in C itself) and the assign it through an (output) operand, e.g.

    unsigned char reg1;
    __asm__(
      "lds  %[r1], 55 \n\t"
      : [r1] "=r" (reg1)
    );

    Don't worry about memory usage: the optimizer finds out that such variable is not used and will not allocate any memory for it.

    One of the often needed operation is to access real C variables. The variable has to have statically allocated memory (has to be global or local static). (There may be a way how to determine the offset of a local variable in the stack frame through an operand - I have not tried this so far). There are several ways how to achieve that:

    • use the variable name directly in asm to load/store its value
      unsigned char blah;
      __asm(
        "lds  r16, blah \n\t"
        :
        :
        : "r16"
      );

      The name will be resolved by the linker, so the variable does not need to be local to the file where the __asm__() snippet is. Expressions involving constants offsets (which are resolved by the linker, too) are also allowed. Note that clobbering directly a register is a "bad thing", as explained by the "cookbook" and above; apart of that, this method "spares" down an operand, if this is desired

    • use the direct address to load a pointer (which is subsequently used in the assembler) (in case of "function pointers", the pm() and gs() assembler operators should be used appropriately).
      unsigned char array[4];
      __asm__(
        "ldi  r30, lo8(array) \n\t"
        "ldi  r31, hi8(array) \n\t"
        "ld   r16, z"
        : 
        : 
        : "r16", "r30", "r31"
      );

      This is very similar to the previous, and useful to process arrays or structs in the __asm__().

    • perform any of the two methods above, except instead of direct entering of variable name supply it through a "constant" constraint operand, e.g.
      unsigned char array[4];
      __asm__(
        "ldi  r30, lo8(%[array_]) \n\t"
        "ldi  r31, hi8(%[array_]) \n\t"
        "ld   r16, z \n\t"
        : 
        : [array_] "X" (&array)
        : "r16", "r30", "r31"
      );

      (there is little benefit in this method, unless the address is computed compile-time, e.g. member of a struct with an offset)

    • instruct the compiler to set aside a register pointer pair and fill it by the value of pointer to the variable:
      unsigned char array[4];
      __asm__(
        "ld   r16, %a[arr] \n\t"
        : 
        : [arr]  "e" (array)
      );

      This is useful in case the __asm__() snippet is going to perform some operation over an array or a struct; this is also useful for local variables (the compiler knows how to calculate the address of variable on the stack frame into a register pair)

    • instruct the compiler to set aside a register (registers) and load the variable into it -- this is the purpose of the "register" type of constraints (input or output or both, depending how the __asm__() is going to use the variable), and this is also the preferred method allowing to the compiler to perform optimizations at its will
      unsigned char array[4];
      __asm__(
        "  ; variable from array[2] already loaded into %[r1] \n\t"
        : 
        : [r1] "r" (array[2])
      );
      

  4. Note, that the compiler does not "see" accesses to a variable in asm, especially if they are performed through a pointer. (This is quite similar as when the compiler does not "see" accesses to a variable in an interrupt service routine). So the optimizer sometimes removes completely other accesses to this variable if it finds them unnecessary (e.g. write accesses in C to variables which read only in asm), and in case of local variables it can completely remove the variable from memory (and keep it only in registers, if it sees this sufficient). This is a good thing in the case of the temporary variables created only to allocate registers (as mentioned above), but a bad thing in case of "real" variables. The "volatile" keyword helps in these cases to "materialize" the variables as needed.

    The cookbook also mentions a special "%-token", namely "%=" (the "%" character got lost here and there in the cookbook unfortunately). This is replaced by the compiler by a number which is unique for every __asm__() statement. Typical use is for labels local to the __asm__() statement even if the it is defined as a macro and repeatedly used in the same file. Beware, as the replacement is a number, if in its usage is combined with more digits, repeated labels can occur inadvertently (i.e. surround the "%=" by non-digit characters). Beware, the compiler is quirky and won't perform this replace until at least the first colon hinting that there are some operands is present (no actual operands are needed). Of course, for jumps, the *nix-style "forward/back" local labels can be used, too.

  5. assembler

    Except for the one-instruction-per-line problem and replacement of "%-tokens", both described in previous text; the assembler syntax and semantics follows the same rules as avr-as - naturally, as the outcome IS assembled by avr-as. Unlike the standalone ".S" file solution, there is no preprocessing of the ".s" coming out of compilation, so any macro one desires to use has to be treated using the features mentioned above. Also the usual gotcha applies when attempting to use labels to be fixed by linker (e.g. names of variables or functions) in expressions - only constant additions are allowed, plus the hi8/lo8-pm-gs kind of operators.

    It is worth noting that all avr-as directives can be used within inline assembler, of course with proper caution, not to "spoil" anything assumed by the compiler (i.e. current section is .text, addresses aligned to even, etc.). Although the inline assembler can be used only within C functions, i.e. they are placed into ".text" section, this does not exclude changing sections - provided .text and .align is restored by the end of the __asm__().

    Locality of labels of all kinds should be also observed and globality can be enforced. Jumps between inline asm snippets is not excluded, but sounds like shooting onself straight into leg.

Comments, please.

Jan Waclawek

[edited twice, "sources" of previous versions and current are in the attached zipfile]

Attachment(s): 

Last Edited: Mon. May 9, 2011 - 08:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Comments, please.
First comment, without having the time at the moment to read the whole thing: it needs to be moved to the tutorials forum. Leaving it here, it will fall off the first page or two too quickly and lessen its usefulness.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OR, a Sticky!

Worth while keeping around, definitely!

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

OR, a Sticky!

After just a quick glanse, this really seems to be tutorial material! I've never cared for the details of inline assembly before, since I've never needed it, but something that is as well written as this seems to be always wettens my appetite.

My priorities for new stuff that I want to learn re GNU development tools had things like linker scripts and Autotools higher prioritized, but I guess I'll have to read this "while it's hot"! :D

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, this is rough, rather than well written. This is why I asked for comments.

Second, I'd rather see mature portions of this to be included into the "cookbook", rather than remain a standalone, ehm, whatever.

Thanks for the patting on shoulder; I like it very much :-P

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Second, I'd rather see mature portions of this to be included into the "cookbook", rather than remain a standalone, ehm, whatever.

Make sense.

Quote:

This is why I asked for comments.

Expect my comments in 24 hours. (In this particular subject I am a noob and not "stained" or "tainted".)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
Expect my comments in 24 hours. (In this particular subject I am a noob and not "stained" or "tainted".)

Johan, so humble. :D Very unusual indeed.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Johan, so humble. [Very Happy] Very unusual indeed.

LOL! Yes, cherish the moment, and tell your grandchildren about it some day!

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Johan,

I am not sure that it's an entertaining bedside reading. You might want to leave it for the moment when the circumstances will press you into reading it. Also it assumes some familiarity with the "cookbook" material and with the avr-as syntax. And perhaps a willing to experiment a bit (which again is best done under pressure in my experience). As I said, it's not a well-written tutorial, just a few thoughts.

But thanks again for your comments... in whatever spirit they are.

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
[*] The exact effect of "volatile" keyword in conjunction with "__asm__" keyword is different from the expectation (namely as a "code barrier", preventing reordering of code), is poorly documented, and appears to have changed during time.
On both variables and on __asm__,
volatile says to not ignore accesses even if they appear to do nothing that matters.
Applying the principle that the programmer has a clue,
an __asm__ that appears to do nothing at all is implicitly volatile.
An unreachable __asm__ may be deleted regardless of volatile.
Quote:
Similarly the effect of "memory" clobber on code reordering. If code reordering across __asm__() statements, and reordering of __asm__() statements themselves, is to be prevented (which is usually the case), at least "verbose" shall be deployed and the result shall be checked for correctness (the brave may also pester the high esteemed gcc developers for remedy).
I think that reordering can be reduced by using input/output parameters.
An input/output parameter should pin the __asm__ between the correct two accesses to the variable specified.
The parameter need not be used.
Quote:
[*] The inline assembler syntax is ugly, too verbose and hard to edit.
Too verbose?
Quote:
[*]the clobbers are described in the "cookbook" in sufficient detail
Actually, I'm a bit fuzzy on what can go there.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
wek wrote:
[*] The exact effect of "volatile" keyword in conjunction with "__asm__" keyword is different from the expectation (namely as a "code barrier", preventing reordering of code), is poorly documented, and appears to have changed during time.
On both variables and on __asm__,
volatile says to not ignore accesses even if they appear to do nothing that matters.
Applying the principle that the programmer has a clue,
an __asm__ that appears to do nothing at all is implicitly volatile.

As so often, I am not sure what your hexa(decimal)meters exactly mean ;-) What access does __asm__() represent? Accesses of its operands? Isn't their "volatileness" determined at the moment when they are defined?

I am afraid the detialed answers rely on intimate knowledge of gcc's code, which I don't intend to acquire, ever. Of course I am ready to rephrase or omit whatever needs to be rephrased or omitted in the above text.

Quote:
An unreachable __asm__ may be deleted regardless of volatile.
This makes sense and I don't doubt that.

Quote:
I think that reordering can be reduced by using input/output parameters.
An input/output parameter should pin the __asm__ between the correct two accesses to the variable specified.
The parameter need not be used.
This again makes sense. However, the problem with reordering may occur when no variable is actually accessed. The quintessential example is with cli()/sei() (i.e. __asm__("cli") etc.); but similar can be constructed e.g. with an access to a SFR which one doesn't want to be delayed by reordering it with a lengthy operation (e.g. call of an intrinsic function).

Quote:
Quote:
[*] The inline assembler syntax is ugly, too verbose and hard to edit.
Too verbose?

This is my bad English. How to describe this particular source of grief? I mean, you have to write too many characters, some of them (both the double-quotes and the backslash) unpleasant to type again and again; especially in comparison to "plain" assembler.

Some of the other 8-bit C compilers feature a more "normal" syntax for assembler, basically equal to the standard syntax enclosed within two special keywords (e.g. asm - endasm). They may be not as powerful as the gcc inline assembler is, but that's unrelated to the particular syntax.

But maybe that's just me. When I write programs, I often have long periods of inactivity just hanging around and perhaps writing down something here and there; and then a sudden surge when I feel an urge to transfer a lot of instructions from myself into the computer. I learned to touch-type (albeit poorly) just to stop hurting my fingers when this happens. This is quite hard to achieve with the standard assembler syntax, but completely impossible with the gcc inline assembler. I tried to write "plain" assembler and the post-process it into the inline syntax - writing a simple command-line tool to achieve that - but that's not quite it, as the "surge" often happens during debugging, and those simplistic tools are not quite helpful with that.

When David says gobbledygook, I believe part of it is on account of those double-quotes and escaped characters.

Quote:
[*]the clobbers are described in the "cookbook" in sufficient detail
Actually, I'm a bit fuzzy on what can go there.
I am talking about http://www.nongnu.org/avr-libc/u... . That subchapter IMHO covers exhaustively the "rXX" clobbers as well as the "memory" clobber, although it contains some extra material, too.

Thank you for your insightful comments.

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
skeeve wrote:
wek wrote:
[*] The exact effect of "volatile" keyword in conjunction with "__asm__" keyword is different from the expectation (namely as a "code barrier", preventing reordering of code), is poorly documented, and appears to have changed during time.
On both variables and on __asm__,
volatile says to not ignore accesses even if they appear to do nothing that matters.
Applying the principle that the programmer has a clue,
an __asm__ that appears to do nothing at all is implicitly volatile.

As so often, I am not sure what your hexa(decimal)meters exactly mean ;-) What access does __asm__() represent? Accesses of its operands? Isn't their "volatileness" determined at the moment when they are defined?
I took it as given that to "access" code means to execute it just as to access a variable means to fetch or assign it.

In both cases, volatile means to do something even though the something seems to have no effect.
In the case of variables, it also means to do the accesses in the order specified regardless of apparent need.

Quote:
Quote:
I think that reordering can be reduced by using input/output parameters.
An input/output parameter should pin the __asm__ between the correct two accesses to the variable specified.
The parameter need not be used.
This again makes sense. However, the problem with reordering may occur when no variable is actually accessed. The quintessential example is with cli()/sei() (i.e. __asm__("cli") etc.); but similar can be constructed e.g. with an access to a SFR which one doesn't want to be delayed by reordering it with a lengthy operation (e.g. call of an intrinsic function).
Don't use SFRs just to try to pin an __asm__.
It won't work and it will cost cycles.

Suppose the problem is fixing:

unsigned q=sqrt(b);
unsigned s, c;
cli();
s=two_byte_volatile;
sei();
c=3*s;
...
use q much later

A problem is to ensure that the specified assignment is done with interrupts disabled.
Another is to ensure that no more computation than specified is performed with interrupts disabled.
Making an SFR an input/output __asm__ parameter would accomplish the former,
but it would cost two cycles.
Instead, make s an input/output parameter.
That will pin the cli() and sei() substitutes to the correct sides of the assignment.
Make q an input/output parameter of the cli() substitute.
Make c an input/output parameter of the sei() substitute.
The cli() and sei() substitutes are rather well pinned.

Quote:

Quote:
Quote:
[*] The inline assembler syntax is ugly, too verbose and hard to edit.
Too verbose?

This is my bad English. How to describe this particular source of grief? I mean, you have to write too many characters, some of them (both the double-quotes and the backslash) unpleasant to type again and again; especially in comparison to "plain" assembler.
Your English is correct.
My threshold is just higher.
Quote:
Some of the other 8-bit C compilers feature a more "normal" syntax for assembler, basically equal to the standard syntax enclosed within two special keywords (e.g. asm - endasm). They may be not as powerful as the gcc inline assembler is, but that's unrelated to the particular syntax.
Its syntax has everything to do with its power.
You can't use a C variable if you have no way to specify it.
I had a brief struggle with IAR's lack of power.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So should I move this to "Tutorial" or not?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I promised my comments in 24 hours, so I'm already late and thus must post this intermediate:

I'm only a part of the way through the text. I find that when I reach the "constraints part", I am immediately sucked into the activity of trying to put together an understanding of how this works. It might be that the text in the OP was a way to simplify this learning experience, but I'm far from there yet.

I'll try to take a deeper dive during easter (fearing that I will spend it all learning avr-gcc assembler... :roll:)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
Quote:
Quote:
[*] The inline assembler syntax is ugly, too verbose and hard to edit.
Too verbose?
How to describe this particular source of grief? I mean, you have to write too many characters, some of them (both the double-quotes and the backslash) unpleasant to type again and again; especially in comparison to "plain" assembler.
To avoid retyping quotes and backslashes,
try using "{:" and ":}".
After you are otherwise done, replace "{:" with a quote and replace ':}' with '\n\t"'.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Emphatically yes; add wek's article to Tutorials!

I'd also hoped to provide feedback sooner, but the sheer volume of informaton wek's packed into it took awhile even to skim.

The article goes a very long way to dispel the confusion of using gcc asm constraints, but I noticed one area I still don't think would have helped me avoid making one of my more recent inline assembler mistakes. I had some use for unpacking a byte's nybbles, and after finding that the first few attempts to encourage the compiler to use a swap instruction only worsened the generated code, I wrote an #include file containing this macro:

// swap.h - A way to have the "Swap" intruction used
//
#ifndef Swap
#define Swap(byteVar) asm volatile( "swap %[BV]" : [BV] "=r" (byteVar) )
#endif

The first couple uses I made of this macro went exactly as I intended, and all was good. Then I tried using it on one of the input arguments of a routine, producing a situation roughly like this:

void frubklatch(uint8_t snarb)
{
   // various statements not involving "snarb"
   Swap(snarb);
   PORTD = snarb;
}

, and the generated result was such that the register that carried the subroutine input argument "snarb" got used as a scratch location without its contents being saved, so that by the time my "Swap" macro got to it, it was effectively garbage. In my ignorance of "assembler constraints", I had assured the compiler that my asm() statement wrote to the "snarb" variable without caring what its original value was. Since nothing referenced 'snarb' prior to the Swap() macro, the compiler decided that the value passed into the subroutine wasn't useful.

Changing the '=' sign in the inline asm to a '+' cured the problem.

Wek covers this issue in his statement,

Quote:
There must also be an extra character just before the constraint letter: "+" if the same variable is to be read before and written after __asm__(),
, or at least I presume he does, but I really can't parse what's meant by "read before and written after".

If the advice had been written as: "Prefix a '+' character to the constraints of 'output' variables whose previous values are essential to the asm() construct", or something?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
The exact effect of "volatile" keyword in conjunction with "__asm__" keyword is different from the expectation (namely as a "code barrier", preventing reordering of code), is poorly documented, and appears to have changed during time.
The documentation is quite clear, it has the same effect as volatile in variables, it state volatile asm statements may not be reordered in significant ways, and the C standard states that only the ordering of access to volatile variables and standard library functions is defined.
wek wrote:
Similarly the effect of "memory" clobber on code reordering.
That's also well defined, accesses to variables that a asm statement access without using undefined behaviour may not be moved past the asm statement.
wek wrote:
If code reordering across __asm__() statements, and reordering of __asm__() statements themselves, is to be prevented (which is usually the case)
It usually isn't required, other than the restraints on variable access mentioned above.
wek wrote:
Let's start with "input" operands, as they are simpler. There are basically two types of constraints: "constants" and "registers".
And "memory" constraints.
wek wrote:
Sometimes the algorithm in the inline asm snippet needs an extra register or two above those allocated by the "register" type of operands. While these can be explicitly named as "r16" etc. and then listed in the clobbers, this is a "bad thing" to do, as it prevents the compiler to chose an optimal register (e.g. unused at the moment by the surrounding code). The way how to do this "properly" is to define a C variable and the assign it through an (input) operand, e.g.
unsigned char reg1;
__asm__(
  "lds  %[r1], 55 \n\t"
  : 
  : [r1] "r" (reg1)
);

No, as it is modified it needs to be an output operand.
wek wrote:
Note, that the compiler does not "see" accesses to a variable in asm, especially if they are performed through a pointer. (This is quite similar as when the compiler does not "see" accesses to a variable in an interrupt service routine). So the optimizer sometimes removes completely other accesses to this variable if it finds them unnecessary (e.g. write accesses in C to variables which read only in asm), and in case of local variables it can completely remove the variable from memory (and keep it only in registers, if it sees this sufficient). This is a good thing in the case of the temporary variables created only to allocate registers (as mentioned above), but a bad thing in case of "real" variables. The "volatile" keyword helps in these cases to "materialize" the variables as needed.
Merely declaring the variable won't do that, as non-volatile asm statements may move past access to volatile variables. The proper solution is to declare the variables as memory operands (not pointers to them), or if this isn't feasible to add "memory" to the clobber list. Likewise declaring a variable as an operand does not permit keeping or modifying pointers to them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
But the avr-as assembler requires a single instruction on a line, so whatever goes after the "ldi r16, 3" sees as "garbage at end of line"
avr-as accepts '$' as command separator.

void foo() {
__asm__(
        "ldi  r16, 3  $ ldi  r17, 5  $"
        "ldi  r24, 6  $"
        );
}

Source by -S :

foo:
/* prologue: function */
/* frame size = 0 */
/* #APP */
 ;  11 "inla.c" 1
	ldi  r16, 3  $ ldi  r17, 5  $ldi  r24, 6  $
 ;  0 "" 2
/* epilogue start */
/* #NOAPP */
	ret

and avr-objdump output:

00000000 :
   0:	03 e0       	ldi	r16, 0x03	; 3
   2:	15 e0       	ldi	r17, 0x05	; 5
   4:	86 e0       	ldi	r24, 0x06	; 6
   6:	08 95       	ret

wbr, ReAl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The documentation is quite clear, it has the same effect as volatile in variables, it state volatile asm statements may not be reordered in significant ways,

Err no - see many (lengthy!) previous threads about this. The main culprit are cli() and sei() which do not always appear at the exact point the user expected.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
unsigned char trombones[76];

void func(void)
{
...
asm(....);  // fill trombones with a pattern
            // best generated with assembly
...
asm(....);  // select and update one trombone
...
}

Can either be done inline without putting memory in the clobber section?

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I modified the initial post to reflect some of the comments (for which I thank to all). The original version is preserved in the zipfile, so you can compare for differences (mainly in the paragraph where I spat venom on volatile/reordering :-) )

I don't claim I understand everything to the last detail, and I am open to further discussion, suggestions, etc. (except that I don't have much more time to burn on this :-( )

TimothyEBaldwin wrote:

wek wrote:
Sometimes the algorithm in the inline asm snippet needs an extra register or two above those allocated by the "register" type of operands. While these can be explicitly named as "r16" etc. and then listed in the clobbers, this is a "bad thing" to do, as it prevents the compiler to chose an optimal register (e.g. unused at the moment by the surrounding code). The way how to do this "properly" is to define a C variable and the assign it through an (input) operand, e.g.
unsigned char reg1;
__asm__(
  "lds  %[r1], 55 \n\t"
  : 
  : [r1] "r" (reg1)
);

No, as it is modified it needs to be an output operand.

The (local) variable is intended only as a "placeholder" for the register, *never* to be used in C (the asm code is of course bogus; please suggest better if it is misleading). I expect that the optimizer will recognize this and will remove the variable altogether except for the register allocation. My experience appears to confirm this.

That's why I believe it should be set as input operand to __asm__().

TimothyEBaldwin wrote:
wek wrote:
Note, that the compiler does not "see" accesses to a variable in asm, especially if they are performed through a pointer. (This is quite similar as when the compiler does not "see" accesses to a variable in an interrupt service routine). So the optimizer sometimes removes completely other accesses to this variable if it finds them unnecessary (e.g. write accesses in C to variables which read only in asm), and in case of local variables it can completely remove the variable from memory (and keep it only in registers, if it sees this sufficient). This is a good thing in the case of the temporary variables created only to allocate registers (as mentioned above), but a bad thing in case of "real" variables. The "volatile" keyword helps in these cases to "materialize" the variables as needed.
Merely declaring the variable won't do that, as non-volatile asm statements may move past access to volatile variables. The proper solution is to declare the variables as memory operands [...]

So, isn't it enough to declare also the __asm__() as such volatile here, to prevent reordering you are talking about? We are talking about arrays and structs and similar here, so they can't be simply declared as input/output operands to __asm__() I presume?

Jan Waclawek

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
TimothyEBaldwin wrote:

wek wrote:
Sometimes the algorithm in the inline asm snippet needs an extra register or two above those allocated by the "register" type of operands. While these can be explicitly named as "r16" etc. and then listed in the clobbers, this is a "bad thing" to do, as it prevents the compiler to chose an optimal register (e.g. unused at the moment by the surrounding code). The way how to do this "properly" is to define a C variable and the assign it through an (input) operand, e.g.
unsigned char reg1;
__asm__(
  "lds  %[r1], 55 \n\t"
  : 
  : [r1] "r" (reg1)
);

No, as it is modified it needs to be an output operand.

The (local) variable is intended only as a "placeholder" for the register, *never* to be used in C (the asm code is of course bogus; please suggest better if it is misleading). I expect that the optimizer will recognize this and will remove the variable altogether except for the register allocation. My experience appears to confirm this.

That's why I believe it should be set as input operand to __asm__().

With good optimization, either should produce the same result.
In case of not so good optimization, I would use an output operand.
It just seems to me that a useless copy from an uninitialized variable is more likely to be missed than an unused result.
In either case, I might be inclined to put the whole thing in braces to explicitly limit the scope of reg1.
Quote:

TimothyEBaldwin wrote:
wek wrote:
Note, that the compiler does not "see" accesses to a variable in asm, especially if they are performed through a pointer. (This is quite similar as when the compiler does not "see" accesses to a variable in an interrupt service routine). So the optimizer sometimes removes completely other accesses to this variable if it finds them unnecessary (e.g. write accesses in C to variables which read only in asm), and in case of local variables it can completely remove the variable from memory (and keep it only in registers, if it sees this sufficient). This is a good thing in the case of the temporary variables created only to allocate registers (as mentioned above), but a bad thing in case of "real" variables. The "volatile" keyword helps in these cases to "materialize" the variables as needed.
Merely declaring the variable won't do that, as non-volatile asm statements may move past access to volatile variables. The proper solution is to declare the variables as memory operands [...]

So, isn't it enough to declare also the __asm__() as such volatile here, to prevent reordering you are talking about? We are talking about arrays and structs and similar here, so they can't be simply declared as input/output operands to __asm__() I presume?
Some of them could,
but one is likely to get extra cycle-consuming copies.
Also, what would one do with

unsigned trombones[76];

'Twould be nice to be able to put trombones in the clobber section.
Perhaps this would work?

{
unsigned * volatile ptr=trombones;
__asm__ volatile ( "..."
 : "=r" (ptr) : "0" (ptr) );
*((volatile unsigned *)ptr);
}

Though I think that there would still be more copying than desired.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
wek wrote:
TimothyEBaldwin wrote:

wek wrote:
Sometimes the algorithm in the inline asm snippet needs an extra register or two above those allocated by the "register" type of operands. While these can be explicitly named as "r16" etc. and then listed in the clobbers, this is a "bad thing" to do, as it prevents the compiler to chose an optimal register (e.g. unused at the moment by the surrounding code). The way how to do this "properly" is to define a C variable and the assign it through an (input) operand, e.g.
unsigned char reg1;
__asm__(
  "lds  %[r1], 55 \n\t"
  : 
  : [r1] "r" (reg1)
);

No, as it is modified it needs to be an output operand.

The (local) variable is intended only as a "placeholder" for the register, *never* to be used in C (the asm code is of course bogus; please suggest better if it is misleading). I expect that the optimizer will recognize this and will remove the variable altogether except for the register allocation. My experience appears to confirm this.

That's why I believe it should be set as input operand to __asm__().

With good optimization, either should produce the same result.
In case of not so good optimization, I would use an output operand.
It just seems to me that a useless copy from an uninitialized variable is more likely to be missed than an unused result.
In either case, I might be inclined to put the whole thing in braces to explicitly limit the scope of reg1.
It should be an output operand.
From another thread and from experiment,
making something an input-only operand is an implicit promise not to change its register(s).

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
It [a dummy variable supplying a register to __asm__()] should be an output operand.
From another thread and from experiment,[...]

Michael,

Can you be more specific, please?

Thanks,

Jan

[EDIT] Thinking more about it: while it is a good remark in general to be added to the text on input operands, I still don't think it invalidates using input operands for using dummy C variables to allocate registers for __asm__(). As the variable is never used (i.e. never assigned) in C, C can't count on its content after __asm__(), regardless of whether __asm__() changes it or not. Please provide arguments against this assertion.

At the same time I don't say it's not a better practice to use output operands for this purpose and if you tell there are no side effects to this practice I will change this in the "main" text; I just like less the output operands than the input ones ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
skeeve wrote:
It [a dummy variable supplying a register to __asm__()] should be an output operand.
From another thread and from experiment,[...]
Can you be more specific, please?
The test data is at the bottom.
Quote:
[EDIT] Thinking more about it: while it is a good remark in general to be added to the text on input operands, I still don't think it invalidates using input operands for using dummy C variables to allocate registers for __asm__(). As the variable is never used (i.e. never assigned) in C, C can't count on its content after __asm__(), regardless of whether __asm__() changes it or not. Please provide arguments against this assertion.
The problem isn't the variable,
the problem is the register.
The compiler could be mistaken about what is in the register.
unsigned char ivan=66, jane;
asm ("..." : "=r" (ivan) : "r" (jane));

jane could be assigned any register at all,
most likely the one assigned ivan.
If ivan were already a register variable,
no prologue or postlogue would be required.

Quote:
At the same time I don't say it's not a better practice to use output operands for this purpose and if you tell there are no side effects to this practice I will change this in the "main" text; I just like less the output operands than the input ones ;-)
With this, avr-gcc does the wrong thing.
int fred, greg;

void func(void)
{
asm volatile (" DEC %A0" : : "r" (0+greg));
fred=greg;
}

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The following drifts a bit off the topic, but once it has been mentioned...

On the ideal syntax of inline assembler.

skeeve wrote:
Quote:

Quote:
Quote:
[*] The inline assembler syntax is ugly, too verbose and hard to edit.
Too verbose?

This is my bad English. How to describe this particular source of grief? I mean, you have to write too many characters, some of them (both the double-quotes and the backslash) unpleasant to type again and again; especially in comparison to "plain" assembler.
Your English is correct.
My threshold is just higher.
Quote:
Some of the other 8-bit C compilers feature a more "normal" syntax for assembler, basically equal to the standard syntax enclosed within two special keywords (e.g. asm - endasm). They may be not as powerful as the gcc inline assembler is, but that's unrelated to the particular syntax.
Its syntax has everything to do with its power.
You can't use a C variable if you have no way to specify it.
I had a brief struggle with IAR's lack of power.

My "ideal" syntax would not restrict writing "traditional" assembler just to please the C parser.

My "ideal" syntax would attempt to be intuitive in the "binding" of C variables (and constants) to registers.

Something along the following lines:

unsigned char var_c;
typedef struct {
  int a, b, c;
} t_my_struct;
t_my_struct s;

__asm__(
  const register A = var_c,  // const is promising the register won't change
  const value K = offsetof(c, t_my_struct),
  register_upper R,  // asking C to allocate a register
)
  ldi  R, K  ;asm comments delimited by semicolon as usual
  add  R, A
  [etc.]
__endasm__(
  var_c = R,
);

This does not take into account the need for reuse/parametrisation of inline asm snippets, which now happens through defining these snippets as preprocessor macros; but I am confindent this could be solved better with a bit of thinking, too.

Now of course I this would require a substantial budget (time == money) to be implemented in gcc. I was experimenting with SDCC a couple of months ago and it's certainly more accessible to experiments; nevertheless it's still a function of the two (usually infinitesimally small) quantities mentioned above... :-(

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
With this, avr-gcc does the wrong thing.
int fred, greg;

void func(void)
{
asm volatile (" DEC %A0" : : "r" (0+greg));
fred=greg;
}

This produces
void func(void)
{
  asm volatile ("DEC %A0" : : "r" (0+greg));
  9e:	80 91 00 01 	lds	r24, 0x0100
  a2:	90 91 01 01 	lds	r25, 0x0101
  a6:	8a 95       	dec	r24
  fred=greg;
  a8:	90 93 03 01 	sts	0x0103, r25
  ac:	80 93 02 01 	sts	0x0102, r24
}
  b0:	08 95       	ret

Perhaps I'm missing something but the generated code doesn't look wrong to me given that you neglected to tell the compiler that you modified a register containing the input operand. Perhaps you could show the code you think should be generated.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
My "ideal" syntax would not restrict writing "traditional" assembler just to please the C parser.
The string thing is could probably be eased just by allowing newlines in asm strings.
Quote:
My "ideal" syntax would attempt to be intuitive in the "binding" of C variables (and constants) to registers.

Something along the following lines:

unsigned char var_c;
typedef struct {
  int a, b, c;
} t_my_struct;
t_my_struct s;

__asm__(
  const register A = var_c,  // const is promising the register won't change
  const value K = offsetof(c, t_my_struct),
  register_upper R,  // asking C to allocate a register
)
  ldi  R, K  ;asm comments delimited by semicolon as usual
  add  R, A
  [etc.]
__endasm__(
  var_c = R,
);

Looks okay for inline AVR assembly,
but making it work generically for lots of assemblers might be tough.
Quote:
This does not take into account the need for reuse/parametrisation of inline asm snippets, which now happens through defining these snippets as preprocessor macros; but I am confindent this could be solved better with a bit of thinking, too.
I think that part of the problem is the target audience.
If one assumes that an inline assembler user is someone that
  • writes operating systems or
  • writes compilers or
  • is a glutton for punishment
then the lack of mundane-friendliness is understandable.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dkinzer wrote:
Perhaps I'm missing something but the generated code doesn't look wrong to me given that you neglected to tell the compiler that you modified a register containing the input operand. Perhaps you could show the code you think should be generated.
By "the wrong thing", I meant not satisfying the purpose under discussion.
To me and to wek, the documentation does not make clear that one is required to preserve an input register,
hence discussion of whether one should allocate
a scratch register as an input or an output.
The answer is an output, probably "=&r"

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
To me and to wek, the documentation does not make clear that one is required to preserve an input register
Perhaps it could be expressed more explicitly. My working assumption has been that the compiler assumes that no registers change across an asm directive except those mentioned in the clobbers list and the output list.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

all,
my head hurts when I try and use the inline assembler for anything that is not very basic. My usual approach is to create a separate asm function that I call.

I had a situation where I wanted to use no stack and 1 register to load the SP. With the help of the examples above I managed to create a simple macro that accesses the value of #define'd values.

Probably trivial to some experts but it wasn't for me :oops:

#define idleStack RAMEND
#define initialiseStack() \
	asm volatile( \
		"ldi  r16, hi8(%[_SP]) \n\t" \
		"out __SP_H__, r16	\n\t"    \
		"ldi  r16, lo8(%[_SP]) \n\t" \
		"out __SP_L__, r16	\n\t"    \
		:                            \
		: [_SP] "X" (idleStack)      \
		:                            \
		);

I know I could have just put SP=idleStack but this used two registers and I only wanted to use one! See below for the code.

		initialiseStack();
 612:	04 e0       	ldi	r16, 0x04	; 4
 614:	0e bf       	out	0x3e, r16	; 62
 616:	0b e5       	ldi	r16, 0x5B	; 91
 618:	0d bf       	out	0x3d, r16	; 61
		SP = idleStack;
 264:	8f e5       	ldi	r24, 0x5F	; 95
 266:	94 e0       	ldi	r25, 0x04	; 4
 268:	9e bf       	out	0x3e, r25	; 62
 26a:	8d bf       	out	0x3d, r24	; 61

regards
Greg

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

instead of

__asm__( 
      "sbic  %[_PIND], 6 \n\t" 
      : 
      : [_PIND]    "I"  (_SFR_IO_ADDR(PIND)) 
    );

you can write

__asm__ __volatile ( 
      "sbic %i[pind], 6" 
      :: [pind] "n" (&PIND) 
);

All currently supported GCC versions know %i modifier, cf. the GCC release notes. The volatile must not be dropped here because the asm has operands.

avrfreaks does not support Opera. Profile inactive.

Last Edited: Sun. Jun 16, 2013 - 07:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is a veeery long text. It would better be in a wiki with links and sections and chapters and where the text could be corrected.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I went and started a project at Atmel's "Spaces" so that we can use its wiki for this purpose.

You'd need to create an account there to be able to edit.

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For what problems do you recomment inline assembler?

Can you factor out specufuc routines where avr-gcc is very bad and there is the recommendation to use inline asm?

One field is the 64-bit moves, are there more? For time-critical sequences like EEPROM there are support macros and functions in AVR-LibC so that there's no need to touch inline asm at all.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SprinterSB wrote:
For what problems do you recommend inline assembler?
I don't see inline asm as solution to a certain "class" of problems; rather, as a very valuable tool to users to solve *any* problem which according to their finding/perception/experience requires more control than C can possibly provide.

You can cater to only a limited set of problems through compiler/library features, inevitably; whereas the requirements of real-world programs result in infinite possible instances of need for such control. You can attempt to constrict the users' degree of freedom (as in Arduino, for example), but in this field that approach backfires within the microsecond timeframe... ;-)

I said this oh so many times, among others also in the very first sentence of the opening post of this thread, btw.

JW