assemblyx86inline-assemblyattioports

GNU assembler syntax for IN and OUT instructions


I am writing an inline assembly code to read Real-Time clock. I am loading the register number(4) to be read to 'dl' and loading this to port 0x70. I am trying to read this register value(4) into al. For more information - RTC

asm(
"mov $4, %%dl;"
"out 0x70, %%dl;"
"in %%al, 0x71;"
:
:
:"%al","%dl"
);

I am getting the below error message on compiling the c file that contains this code.

Assembler messages:

Error: operand size mismatch for 'out'

Error: operand size mismatch for `in'

The assembler version : GNU assembler version 2.26.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.26.1

Can someone please point out the issue?


Solution

  • There are a number of problems. First, both in and out can only operate on the A register (al, ax, eax, rax). Second, the operand order is wrong and third, immediates have to be prefixed with $ in AT&T syntax. In plain assembly, your code should look like this:

    mov $4,%al
    out %al,$0x70
    in  $0x71,%al
    

    So your corrected assembly would be

    asm(
    "movb $4, %%al;"
    "outb %%al, $0x70;"
    "inb $0x71, %%al;"
    :
    :
    :"%al"
    );
    

    Note that I have added explicit size suffixes to help clang assemble this code. I recommend you to further alter this code to use correct extended inline assembly:

    unsigned char result;
    volatile asm("outb %1,$0x70; inb $0x71, %0" : "=a"(result) : "a"((char)4));
    

    This could make gcc generate slightly better code. For example, it allows gcc to inline this code.

    Furthermore, you could consider using the inb() and outb() macros from <sys/io.h> to avoid inline assembly altogether. This is typically a very good thing to do.