8

I'm developing a small toy kernel in C. I'm at the point where I need to get user input from the keyboard. So far, I have implemented inb using the following code:

static inline uint8_t inb(uint16_t port) {
     uint8_t ret;
     asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
     return ret;
}

I know that the "=a" constraint means that al/ax/eax will be copied to ret as output, but I'm still confused about the "Nd" constraint. Can anyone provide some insight on why this constraint is necessary? Or why I can't just use a general purpose register constraint like "r" or "b"? Any help would be appreciated.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Michael Morrow
  • 403
  • 2
  • 16

1 Answers1

8

The in instruction (returning a byte) can either take an immediate 8 bit value as a port number, or a port specified in the dx register. More on the in instruction can be found in the instruction reference (Intel syntax) . The machine constraints being used can be found in the GCC docs . If you scroll down to x86 family you'll see:

d

The d register

N

Unsigned 8-bit integer constant (for in and out instructions). 
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Okay, thanks. This helped a lot. I have one final question however, if the `N` constraint only allows an immediate byte for ports from 0-255, but the `d` constraint allows for the entire `dx` register, why wouldn't I use only the `d` constraint? I mean, what are the advantages of using both constraints? – Michael Morrow Sep 26 '15 at 00:03
  • @MichaelMorrow This is a hold over from the 8086/8088 days when saving space was often important. If you use an 8 bit immediate (for a port between 0-255) you don't need the extra instruction to move it to `DX` first (save space). If you wanted to access a port 256-65535 you had to pass it in `DX`. You can specify an 8 bit (0-255) port number in `DX` but you incur the extra penalty in space by having to move it to `DX` first. – Michael Petch Sep 26 '15 at 00:10
  • @MichaelMorrow If you compile with optimizations (-O1, -O2, -O3) using "Nd" as a constraint, the assembler template will give a hint to the compiler if it sees a value being passed can fit in 8 bits and code the smaller version (without moving to `DX` first). If you just used "d" then there is no hint that the compiler can optimize the assembler code in the template and will use the longer form (with `DX` whether required or not). When I get a few minutes later I'll update my answer with all this info. – Michael Petch Sep 26 '15 at 00:36