Input/output operands in GCC inline assembly

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
shahsunny712
Posts: 20
Joined: Wed Jan 29, 2014 11:57 am

Input/output operands in GCC inline assembly

Post by shahsunny712 »

I am writing the inb and outb functions for port I/O.

As part of this, I learnt the basics of inline assembly from various online sources and wrote the following implementation for inb:

Code: Select all

__asm__ __volatile__ ("in %0, %1":"=a"(portval):"Nd"(__port));
Here are my questions:

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 ?

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));
3. What difference does it make if I don't use "N" ? Wouldn't simply using "d" be better, since that is 16 bits ?

4. If I omitted the "N", then is it correct that value of __port is copied to edx and %1 is replaced by 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 ?
User avatar
fluray
Posts: 9
Joined: Thu May 15, 2014 2:04 am

Re: Input/output operands in GCC inline assembly

Post by fluray »

I think disassembly debugging can solve these problems.
You can try to write your code for each question and observe the corresponding disassembled code.
It will show you the hidden things and answer your questions.
User avatar
voidf0xb3
Posts: 7
Joined: Fri Mar 16, 2012 11:32 am
Location: Moscow, Russia

Re: Input/output operands in GCC inline assembly

Post by voidf0xb3 »

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).
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Input/output operands in GCC inline assembly

Post by xenos »

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 ?
More or less. One needs to distinguish between compile time and run time. At compile time the compiler figures out whether __port has a known value < 256, and if this is the case, it emits an instruction with an immediate operand. Otherwise, it emits instructions to copy the value of __port into edx and an instruction using this value. It also knows that after the operation the value of eax should be used as the new value for portval, so it will emit instructions to do whatever should be done with this value. At run time these instructions are executed.
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));
IMHO yes.
3. What difference does it make if I don't use "N" ? Wouldn't simply using "d" be better, since that is 16 bits ?
If you omit "N", and before that code somewhere you set __port to, say, 0x70, the compiler will always use edx, even though here it already knows the value of __port and that it is small enough to be coded as an immediate.
4. If I omitted the "N", then is it correct that value of __port is copied to edx and %1 is replaced by edx ?
Yes.
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 ?
As I wrote above, sometimes the compiler knows the value of __port in advance (for example, if you had an __port = 0x70; just before).
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
shahsunny712
Posts: 20
Joined: Wed Jan 29, 2014 11:57 am

Re: Input/output operands in GCC inline assembly

Post by shahsunny712 »

Thank you all for the detailed replies :D
jbemmel
Member
Member
Posts: 53
Joined: Fri May 11, 2012 11:54 am

Re: Input/output operands in GCC inline assembly

Post by jbemmel »

Linux is typically a good reference: http://lxr.free-electrons.com/source/ar ... /io.h#L272

Note the "%w1" for the port argument, telling the compiler it should be 16-bit
Post Reply