banner



How Do You Tie Inputs And Outputs To Registers In Extended Assembly

constraint

Introduction

I take a confession to make. My previous examples were not very efficient associates lawmaking. That might seem like an odd comment, particularly since my typical example used just 2-4 lines of code. But, these examples were coded as one would write pure assembly, which is not necessarily the way inline should be written. The sneaky assembler silently inserts some extra code into our programs.

Writing code for the inline associates requires a paradigm shift. Starting with this tutorial, we'll brainstorm to cover the odd method of coding input and output operands for the asm statement. Using them volition enable us to produce more efficient inline code.

Extending Inline

Recall the general grade of an extended inline assembler statement is:

asm("code" : output operand list : input operand listing : clobber listing);

This argument is divided by colons into (up to) iv parts. While the code part is required, the others are optional:

  • Code: the assembler instructions, defined as a unmarried cord constant.
  • A list of output operands, separated by commas.
  • A list of input operands, separated by commas.
  • A list of "clobbered" or "accessed" registers.

We previous covered the code portion and the clobber list. We will continue to introduce new assembler instructions with each installment. But now, let'due south talk over the input and output operands.

Constraints

Each input or output operand is described by a constraint string followed by a C expression in parentheses. Constraints are primarily letters but can be numbers too. The selection of the proper constraint depends on the range of registers or constants adequate to the AVR educational activity they are used with. Here'southward an instance:

"=r" (status) : "I" (_SFR_IO_ADDR(PINB)), "I" (PINB5)

Remember, the C compiler doesn't check your assembly code. But the assembler does cheque the constraint against your C expression. If you specify the incorrect constraints, the compiler may silently laissez passer wrong code to the assembler, which would crusade it to fail. And if this happens, everything abruptly terminates, giving a very cryptic error message.

For example, if you specify the constraint "r" and you are using this register with an "ORI" teaching in your code, the assembler may select any register. This would fail if the assembler selects r2 to r15. That's why the right constraint in that instance is "d". Simply, we're getting ahead of our selves.

Constraining Constraints

The assembler is free to select any register for storage of the value that meets the constraints of your constraint. Interestingly, the assembler might non explicitly load or store your value, and it may fifty-fifty decide not to include your assembler lawmaking at all! All these decisions are function of the optimization strategy. For example, if y'all never apply the variable in the remaining part of the C program, the compiler volition most likely remove your code unless you switched off optimization.

Modified Constraints

A modifier sometimes precedes the constraint. Let's demonstrate with an inline assembler routine using a C char variable (a), and an viii-bit constant value (ANSWER_TO_LIFE). Our inline program volition but save the constant to our variable.

Here is our output constraint cord:

"=r" (a)

The equal sign is the modifier; '=' means that this operand is written to past this pedagogy, and the previous value is discarded and replaced by new data. The 'r' is the constraint, and instructs the assembler to place our value into "any general annals".

The input constraint string is fifty-fifty simpler:

"1000" (ANSWER_TO_LIFE)

The 'M' defines an eight-fleck integer constant in the range of 0-255. Inside the parentheses we identify our C MACRO defined value, "ANSWER_TO_LIFE".

Remember, nosotros use the colon character, ':' to separate lawmaking, outputs, inputs and clobbers.

Percentage Null

Take discover of the '%0' and '%1' characters in our inline code below. These represent the commutation locations for the operand values from the constraint strings. The constrained values are substituted into the code in the order they appear at the lesser of the inline routine, 0 beginning, then ane, 2, etc. The kickoff value (a) as constrained, is substituted where '%0' appears, and the number '42' is substituted for '%one'. If in that location were additional constraints, the next i in line would be substituted for '%two', and sequentially onward.

Here is our total code:

#define ANSWER_TO_LIFE 42  volatile uint8_t a;  void setup() {   Serial.begin(9600);    asm (     "ldi %0, %1 \n"     : "=r" (a) : "One thousand" (ANSWER_TO_LIFE)   );      Serial.print("a = "); Serial.println(a); }  void loop() { }        

This is where it gets a little disruptive. The assembler doesn't just replace the '%0' with the value in parenthesis. For our instance, that would result in an assembler instruction looking something like:

ldi a, ANSWER_TO_LIFE

That'due south invalid syntax for the LDI educational activity. Instead, it replaces '%0' with the value as described in the constraint. That's an of import difference. As such, it creates a valid assembler pedagogy like this:

ldi r24, 42

And that works.

Furthermore, discover we haven't included any code to store the output. Rather, nosotros instructed the assembler to do this for u.s. through the equal sign '=', in our "modified constraint". This may seem like an odd way of writing assembler code, just this is how we do it. It seems natural to desire to add a line something similar the following afterwards the LDI command:

"sts %0, %ane \due north"

Y'all could. Only it's simply not necessary.

Getting To Specifics

Output operands must be write-only and the C expression effect must be an lvalue, which means that the operands must be valid on the left side of assignments. Annotation, that the compiler does non check if the operands are a reasonable type for the kind of performance used in the assembler instructions.

Input operands are read-only. Read-write operands are non supported in inline assembler code. But there is a solution to this and we encompass it beneath under the heading of "Straight Ahead".

When the compiler selects the registers to use which correspond the input or output operands, it does not use whatever of the registers listed in the "clobbered" section. As a consequence, clobbered registers are available for any use in the assembler lawmaking.

Be forewarned, that accessing information from C programs without using input/output operands (such as by using global symbols direct from the assembler template) may not work as expected. Since the assembler does not parse the inline code, it has no visibility of any symbols it references. This may result in those symbols being discarded equally unreferenced unless they are also listed every bit input, output operands. The moral of this story is, "Apply INPUT AND OUTPUT OPERANDS."

The Percentage of A and B

Here is another instance of performing a simple bandy between two xvi-bit integers:

volatile int a = 0xa1a2; volatile int b = 0xb1b2;  void setup() {   Serial.begin(9600);    asm (     "mov %A0, %A3 \n"     "mov %B0, %B3 \n"     "mov %A1, %A2 \north"     "mov %B1, %B2 \northward"     : "=r" (a), "=r" (b) : "r" (a), "r" (b)   );    Serial.print("a = "); Serial.println(a, HEX);   Series.print("b = "); Serial.println(b, HEX); }  void loop() { }        

Outset, observe the letters A and B, as in %A0 and %B0. They refer to two different 8-chip registers, each containing a part of the 2-byte value of %0. Recalling, the Arduino is a little-endian microcontroller, meaning the LSB is stored in the lower memory address and the MSB is stored in the higher address. Therefore, 'A' refers to the MSB, while 'B' addresses the LSB. If we were dealing with a 4-byte, 32-bit value, nosotros would employ the messages A through D.

Second, nosotros accost the 2 variables (a) and (b) separately as both input and output operands. When the compiler fixes up the operands to satisfy the constraints, it needs to know which operands are read by the instructions and which are written by information technology. Again, '=' identifies an operand which is simply written ('+' identifies an operand that is both read and written, and all other operands are assumed to be read only).

Cipher Name Your Operands

Although I sometimes find this an boosted layer of confusion placed on tiptop of a topic already layered with confusion, operands tin be given names. The name is pre-pended in brackets to the constraints in the operand list, and references to the named operand use the bracketed name instead of a number after the % sign. Thus, the above instance on the "significant of life" becomes something like this:

asm (   "ldi %[varA], %[Answer] \n"   : [varA] "=r" (a) : [Answer] "Grand" (ANSWER_TO_LIFE) );        

I will leave you to yourself to determine if this makes the inline lawmaking easier to empathize. Accept note, that throughout this tutorial series we never use this characteristic.

Move Over

Terminal, we introduce the MOV instruction. Did you lot guess that MOV is the mnemonic for MOVe? The MOV instruction makes a copy of one annals into some other. The source register is left unchanged.

Allow'southward examine the code the assembler produces for this case. We should accept note that this code is non very efficient:

LDS R24, 0x0102 //a
LDS R25, 0x0103
LDS R18, 0x0100 //b
LDS R19, 0x0101
MOV R18, R18
MOV R19, R19
MOV R24, R24
MOV R25, R25
STS 0x0103, R19
STS 0x0102, R18
STS 0x0101, R25
STS 0x0100, R24

Direct Ahead

Let'southward make it efficient. Using bytes instead of integers, here is the straight-forward inline method for performing a swap. Over again, notice we define both input and output operands. But, for the input operators it is possible to use a unmarried digit in the constraint cord. Using a digit "due north" tells the compiler to employ the same register as the 'northward-thursday' output operand (they start at zero).

Next, hopefully you noticed, in a sneaky way nosotros switched the order of the inputs and outputs. Finally, you probably noticed that we don't write any lawmaking at all. Because our constraints do it for u.s.a.!

uint8_t a = 10; uint8_t b = xx;  void setup() {   Series.begin(9600);    asm (     "" : "=r" (a), "=r" (b) : "0" (b), "ane" (a)    );    Serial.print("a = "); Serial.println(a);   Series.print("b = "); Serial.println(b); }  void loop(void) { }        

The same method works with integers:

volatile int a = 0xa1a2; volatile int b = 0xb1b2;  void setup() {   Serial.begin(9600);    asm volatile(     "" : "=r" (a), "=r" (b) : "0" (b), "1" (a)    );    Serial.impress("a = "); Serial.println(a, HEX);   Serial.impress("b = "); Serial.println(b, HEX); }  void loop() { }        

I More than Clobber

There is a special blazon of clobber called "memory" which informs the compiler that the inline assembly code performs memory reads or writes to items other than those listed in the input and output operands (for example, accessing the memory pointed to by one of the input parameters). This is a "clobber" that is easily missed, and I admit to omitting it often.

To ensure retention contains right values, the compiler may demand to affluent specific registers pointing to retention before executing the inline code. Farther, the compiler does non assume that whatever values read from memory before the inline code remain unchanged afterwards that code (it reloads them as needed). Using the "memory" clobber finer forms a read/write memory barrier for the compiler.

Wrap Up

We covered a lot of material about constraints in this post, and we've merely just begun. The proper use of constraints is critical to writing correct and efficient inline assembly code. Information technology took me hours of studying the constraint listing to become skillful with them, and at times, I notwithstanding get frustrated. But as nosotros go along in this series, it will get easier and clearer. With practice comes proficiency.

AVR family Specific Constraints

constraints

The x register is r27:r26, the y register is r29:r28, and the z register is r31:r30

Modifier Characters

'='
Means that this operand is written to by this educational activity: the previous value is discarded and replaced by new information.

'+'
Ways that this operand is both read and written by the instruction.
When the compiler fixes up the operands to satisfy the constraints, it needs to know which operands are read past the educational activity and which are written by information technology. '=' identifies an operand which is but written; '+' identifies an operand that is both read and written; all other operands are assumed to just be read.
If y'all specify '=' or '+' in a constraint, you put information technology in the commencement character of the constraint string.

'&'
Means (in a particular alternative) that this operand is an earlyclobber operand, which is written earlier the instruction is finished using the input operands. Therefore, this operand may non lie in a annals that is read past the instruction or as function of whatever retentiveness accost.
'&' applies merely to the alternative in which it is written. In constraints with multiple alternatives, sometimes one alternative requires '&' while others do not.
A operand which is read by the instruction can exist tied to an earlyclobber operand if its only apply as an input occurs before the early on result is written. Adding alternatives of this grade ofttimes allows GCC to produce better lawmaking when only some of the read operands can exist affected by the earlyclobber.
Furthermore, if the earlyclobber operand is as well a read/write operand, then that operand is written simply after it'southward used.
'&' does not obviate the need to write '=' or '+'. As earlyclobber operands are always written, a read-only earlyclobber operand is ill-formed and will be rejected by the compiler.

'%'
Declares the teaching to be commutative for this operand and the following operand. This means that the compiler may interchange the two operands if that is the cheapest style to brand all operands fit the constraints. '%' applies to all alternatives and must announced as the kickoff character in the constraint. Only read-only operands can use '%'.
GCC can only handle one commutative pair in an asm; if you use more, the compiler may fail. Note that you demand not utilise the modifier if the two alternatives are strictly identical; this would only waste time in the reload pass.

Reference

AVR viii-bit Instruction Ready
AVR-GCC Inline Assembler Cookbook
Extended Asm – Assembler Instructions with C Expression Operands
Unproblematic Constraints
Automobile Specific Constraints
Constraint Modifiers

As well bachelor equally a book, with greatly expanded coverage!

BookCover
[click on the epitome]

How Do You Tie Inputs And Outputs To Registers In Extended Assembly,

Source: https://ucexperiment.wordpress.com/2016/03/10/arduino-inline-assembly-tutorial-4/

Posted by: walterssweas1972.blogspot.com

0 Response to "How Do You Tie Inputs And Outputs To Registers In Extended Assembly"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel