i'm not sure if i accidently modified the code a bit, but here it is (i'm a beginner to inline assembly, but quite used to assembly in a different file):-
void out8(uint16 port, uint8 data) {asm volatile("outb %0, %1" : "dN"(port) : "a"(data));}
void out16(uint16 port, uint16 data) {asm volatile("outw %0, %1" : "dN"(port) : "a"(data));}
void out32(uint16 port, uint32 data) {asm volatile("outl %%eax, %%dx" : "dN"(port) : "a"(data));}
previously it gave no error but now it is. but can anyone correct this code? and also, tell me on what basis is the colon separating the values, the "dN" and the "a" in the colon separated areas, the "%0" and "%1" in the inline assembly, why are those variables "port" and "data" in brackets next to the "a" and "dN" and what is the difference between "%[reg]" and "%%[reg]" so that i can solve problems like these later when i get them. (tl;du (u stands for understand) the manual page for inline extended assembly is japanese (you know what i mean, right?))
[SOLVED]
used :-
void out(uint16 port, uint8 data) {asm volatile("outb %1, %0" :: "dN"(port), "a"(data));}
void out(uint16 port, uint16 data) {asm volatile("outw %1, %0" : : "dN"(port), "a"(data));}
// Warning, this one's still unsafe, even though it compiles
void out(uint16 port, uint32 data) {asm volatile("outl %%eax, %%dx" : : "dN"(port), "a"(data));}
(Warning to future readers: outl
still has a bug, see the answer(s).)
The manual documents the syntax (and much other info; it's worth reading these days since it's gotten a lot of improvements over the years). https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html.
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
See also https://stackoverflow.com/questions/tagged/inline-assembly for links to guides other than the official docs. Especially https://gcc.gnu.org/wiki/DontUseInlineAsm which describes some pitfalls.
You have "dN"(port)
in the outputs section, but it's actually an input (no =
or +
: docs). And yes, the I/O port number should be an input: it's something the asm statement needs to get from the surrounding code of your program, not something the asm statement is providing back to your program. So the compiler needs to know how to provide it to the asm statement, not collect it.
If you're confused by the fact that out
has two "inputs", think of it like a store instruction. Two pieces of data come from the CPU (the data and the address), resulting in a store to memory. Unlike a load or in
where the load instruction writes a register, in which case the compiler needs to know where that result is placed.
You also have the %0, %1
operands in the wrong order for the order of the constraints, assuming this is for AT&T syntax (not gcc -masm=intel
). Named constraints like [port] "dN" (port)
to match %[port]
in the template would avoid that. https://www.felixcloutier.com/x86/out is out dx/imm8, AL/AX/EAX
in Intel syntax, or out %al/%ax/%eax, %dx/$imm8
in AT&T.
out32()
in another way.A "dN"
constraint allows the compiler to pick DX or an immediate for that variable (if its value is known at compile time and is small enough).
But your asm template string doesn't reference that %0
first operand, instead hard-coding the %%dx
register name, which is only correct if the compiler picks DX.
An optimized build that inlines out32(0x80, 0x1234)
would not have put the port number in DX with preceding instructions, instead picking the N
(unsigned 8-bit) constraint because it's cheaper. But no $0x80
gets filled in anywhere in the final compiler-generated asm because there's no %0
in the template for the compiler to expand.
Think of asm template strings like printf format strings that the compiler expands, before passing on the whole asm to the assembler. (Including compiler-generated instructions before and after, from compiling other C statements, and for some constraints like "r"
or "d"
to put a C variable or expression's value into a register if it wasn't already there.)
%%
is just a literal %
, so if you want to hard-code an AT&T register name like %eax
, you use %%eax
in the Extended Asm template.
You can see that asm on https://godbolt.org/. (Use "binary" mode to see if the resulting compiler-generated asm will actually assemble. That's not guaranteed when using inline asm.)
For working outb
/ etc. macros, many codebases define them, and I think some libc implementations have inline wrappers, like maybe MUSL, maybe also glibc. If you just want working code, don't try to write your own when you don't know inline asm.
Related:
How do you explain gcc's inline assembly constraints for the IN, OUT instructions of i386? breaks down an inb
wrapper function.
C inline asm for x86 in / out port I/O has operand-size mismatch the size of register picked for "d"
depends on the C type width, and has to be 16-bit because in
/out
specifically use DX. (I/O address space is 64k, unlike memory address space.)
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html The manual. Read it. Inline asm is easy to get subtly but dangerously wrong; do not rely on trial and error / "seems to work".
https://gcc.gnu.org/wiki/DontUseInlineAsm. (Or in this case, find some known-good in/out wrapper functions in a libc header or something and use them instead of writing your own, if you don't know inline asm.)