Search code examples
cgccx86inline-assemblyintel-syntax

GCC inline assembly intel syntax "Error: invalid use of register"


I decided to try using inline assembly in c for my basic operating system (using intel syntax because I am used to that with NASM). And I came across something I could not fix, I tried looking everywhere but nothing has worked. while trying the code shown after the error messages, GCC displays these messages:

main.c: Assembler messages:
main.c:8: Error: invalid use of register

The code being:

int main(){
    asm("mov ah, 0x00\n\t");
    asm("mov al, 0x13\n\t");
    asm("int 0x10\n\t");
    asm("mov bx, 0xA000\n\t");
    asm("mov es, bx\n\t");
    asm("mov di, 0\n\t");
    asm("mov byte [es:di], 0x0f\n\t"); // Problem line of error.
    while(1){
        asm("nop");
    }
    return 0;
}

Can someone help me?


Solution

  • Lots to mention about this. First, you are using Intel syntax, which is fine (at least with GCC, it's harder with clang). You can use -masm=intel when you compile it, or as a hack switch to .intel_syntax noprefix inside your asm template, then switch back to .att_syntax at the end of your asm string so the rest of the compiler's code still works. (How to set gcc to use intel syntax permanently?)

    Next, you have lots of asm() blocks. It's probably better to have one, so you can specify inputs / outputs / clobbers / gotos for the entire block. That can be done pretty easily, since C concatenates adjacent strings.

    Your code has NASM-style [es:di] in it. In GAS's dialect of Intel syntax, the segment name goes outside the brackets: es:[di]. I also note that [di] isn't going to be appropriate unless you are on a 16-bit architecture; the thing in square brackets must be the size of a pointer on the platform...

    ...However you are also using int 0x10, which is a BIOS interrupt, so I am guessing that 16-bits is actually what you intend here.

    All that said, you do need to get in the habit of specifying clobbers. That is, specify which registers your code modifies, and whether it modifies the condition code register and memory. The compiler is making decisions about which variables to hold in registers, etc., and has no idea what your assembly code does. You have to tell it you are modifying, for instance, ax, so it knows and can save/restore it.

    GCC doesn't really know about segmentation (it's a portable compiler that assumes a flat memory model, not specializing in x86-16), so it's not really safe to leave es with a different base than ds. e.g. GCC might choose to use rep stosd to initialize a struct or array. It's safe here because your code after the asm is minimal.

    Here's my rewrite of your code.

    int main()
    {
        asm volatile(
            ".intel_syntax noprefix\n\t"
            "mov ah, 0x00\n\t"
            "mov al, 0x13\n\t"
            "int 0x10\n\t"
            "mov bx, 0xA000\n\t"
            "mov es, bx\n\t"
            "mov di, 0x00\n\t"
            "mov byte ptr es:[di], 0x0f\n\t"
            ".att_syntax"                      // undo .intel_syntax
            : /* output operands */
            : /* input operands */
            : "ax","bx","di","cc", "memory" /* clobbers */
        );
        while(1){
            // asm("nop");  // unneeded: empty infinite loops are already legal in C.
        }
        return 0;
    }
    

    If your code modifies or even reads memory that you also access via C pointers, you would also include "memory" in the clobbers line. See the documentation for inline assembly for gcc, and How can I indicate that the memory *pointed* to by an inline ASM argument may be used?

    volatile is not strictly necessary here because with no output operands, the asm statement is implicitly volatile. But it's a good idea to include volatile to make it clear (to human readers) that there's a visible side effect (the BIOS call, and storing to video memory). So if you did include an output operand, it wouldn't get optimized away.

    Addendum: In general, use -masm=intel to compile, instead of using .intel_syntax in the listing. That lets the compiler use the right syntax when substituting operands.