here's a sample code of my kernel using inline assembly:
static __inline__ dword peek(word sel,void *off)
{
dword ret;
asm("push %%es;mov %1,%%es;"
"mov %%es:(%2),%0;"
"pop %%es":"=r"(ret):"g"(sel),"r"(off));
return ret;
}
- try always to use inline functions (will be expanded inside of the caller code and save a lot of stack ops). Cute&pasting assembly code through your kernel will give you headaches.
- the asm() instruction of GCC is split in 3 parts separated by columns "operations":<result storing>:<inputs>
- use "info gcc" if you need to do complex stuffs...
What GCC inline assembly can do for you is selecting registers and operands that will best fits its actual registers usage based on some informations you gave it such as "ret must be a general register", or "my code spills ecx, don't rely its value to remain constant", which saves you to write lot of useless pushes and pops.
now what does my code mean (in nasm):
push es
mov es, <sel=any general register or memory location, left to the appreciation of the compiler>
mov <ret=any general register ...>,[es:<off in any general register>
pop es
one possible code generation if not inlined would be:
push ebp ;;;; skipped if inlined
mov ebp, esp ;;;;
push es
mov es,[ebp+8]
mov edx,[ebp+12]
mov eax,[es:edx]
pop es
pop ebp ;;;;
ret ;;;;
Now if the caller code widely uses edx, the compiler could choose to use ecx instead, or to push/pop edx ... regarding to what it prefers
Just in case, do *always* check the code gcc generated matches the code you wanted to write (at least once), using gcc -S or objdump -d ...