FIrstly, I’m not an expert in AT&T syntax, but I believe that the output operand should go last, not first (so “
in %1, %0”, not “
in %0, %1”).
shahsunny712 wrote:1. Here is my understanding of how this works:
* %1 is replaced with either the immediate value (because of "N") or edx(because of "d"). The immediate/edx get the value from __port.
* %0 is replaced with eax. (because of "a"). I'm not at all sure about this. Since "a" is only an output operand, will %0 be replaced with eax ?
* The instruction is executed.
* The value from eax is copied to portval.
Is this correct ?
Almost. The size of the register that the compiler inserts instead of
%0 and
%1 depends on the size of the operand. So, if
portval is 8-bit, then
%0 becomes
al; if it’s 16-bit, it becomes
ax; if it’s 32-bit, it becomes
eax; and (in 64-bit code) if it’s 64-bit, it becomes
rax.
2. Since %0 is always referring to eax, will this implementation be equivalent:
Code: Select all
__asm__ __volatile__ ("in %%eax, %1":"=a"(portval):"Nd"(__port));
I’d say it’s equivalent as long as
portval is 32-bit.
3. What difference does it make if I don't use "N" ? Wouldn't simply using "d" be better, since that is 16 bits ?
"N" allows the compiler to insert the port number as an unsigned 8-bit immediate, if it knows the value at compile time and the value fits in 8 bits. This avoids using an extra instruction to load the port number into DX, as well as allowing the compiler to use that register for other purposes.
If you look in the Intel manual, you will see that the
in instruction accepts the port number as either an unisgned 8-bit immediate or as an unsigned 16-bit value in DX. That’s exactly what you’re saying with the "Nd" constraint: the compiler can choose either unsigned 8-bit immediate ("N") or the
d register ("d")—which should be DX if
__port is a 16-bit variable.
4. If I omitted the "N", then is it correct that value of __port is copied to edx and %1 is replaced by edx ?
Once again, depends on the size of
__port. Note that the
in instruction only accepts 16-bit port numbers (so it should be
dx, not
edx).
5. The GCC manual says "N" is
Unsigned 8-bit integer constant (for in and out instructions).
But here __port is unsigned short int which I believe would be 16 bits. So how is it decided which portion of the 16 bits is used in the inline assembly ?
The compiler will only pass the value as an unsigned 8-bit immediate if the value of the operand (the actual number stored in the variable
__port) fits in an unsigned 8-bit integer. So, technically, the immediate value is going to consist of the
low byte of
__port (while the high byte is 0).