Search code examples
gccx86x86-64inline-assemblyosdev

When compiled inb and outb in inline assembly produce "Error: operand type mismatch"


I'm trying to code the simplest of the kernels for 64 bits arch and I'm having trouble with keyboard input.

I'm currently implementing this two functions to manage I/O

unsigned char inportb (unsigned short _port)
{
    unsigned char rv;
    __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
    return rv;
}

void outportb (unsigned short _port, unsigned char _data)
{
    __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
}

But I'm getting this assembler error:

main.c: Mensajes del ensamblador:
main.c:51: Error: no coincide el tipo de operando para «in»
main.c:61: Error: no coincide el tipo de operando para «out»

Or in English:

main.c: Assembler messages:
main.c:51: Error: operand type mismatch for `in'
main.c:61: Error: operand type mismatch for `out'

My guess is that this code (that I got from http://www.osdever.net/bkerndev/Docs/creatingmain.htm) is designed for 32 bits assembly.

Any help on how to solve my problem would be greatly appreciated.

I build and run everything with this script

#!/bin/bash

nasm -f bin boot.asm -o boot.bin
nasm -f elf64 loader.asm -o loader.o

#cc -m64  -ffreestanding -fno-builtin -nostdlib -c main.c
cc -m64 -masm=intel -c main.c
ld  -Ttext 0x100000 -o kernel.elf loader.o main.o 
objcopy -R .note -R .comment -S -O binary kernel.elf kernel.bin

dd if=/dev/zero of=image.bin bs=512 count=2880
dd if=boot.bin of=image.bin conv=notrunc
dd if=kernel.bin of=image.bin conv=notrunc bs=512 seek=1

rm ./boot.bin ./kernel.bin ./main.o ./loader.o ./kernel.elf

qemu-system-x86_64  image.bin

Solution

  • By default GCC uses AT&T assembly syntax when generating assembly code from C code. This can be overridden by using the -masm=intel GCC compile option. In the update to your question you have -masm=intel in your GCC command line:

    cc -m64 -masm=intel -c main.c
    

    The code you found was designed for AT&T syntax where the source operand of an instruction is first and the destination is second. -masm=intel option has reversed that behavior. You have two choices. Reverse the operands in the inline assembly so they are destination, source (intel syntax) like this:

    unsigned char inportb (unsigned short _port)
    {
        unsigned char rv;
        __asm__ __volatile__ ("inb %0, %1" : "=a" (rv) : "dN" (_port));
        return rv;
    }
    
    void outportb (unsigned short _port, unsigned char _data)
    {
        __asm__ __volatile__ ("outb %0, %1" : : "dN" (_port), "a" (_data));
    }
    

    The other option is to remove -masm=intel option from your GCC command line and keep the code as is. This might be preferable as a significant amount of OS Development code uses AT&T syntax for inline assembly.


    Note: You might want to consider using gcc instead of just cc