Page 1 of 1
Outb; can you explain why one function works and one doesn't
Posted: Thu Dec 18, 2008 6:26 pm
by Puffy
Hey,
I tried writing my own outb function in asm, and I came up with this:
Code: Select all
out_port_byte:
pushl %ebp
movl %esp, %ebp
movw 6(%ebp), %dx
movb 7(%ebp), %al
outb %al, %dx
popl %ebp
ret
That code doesn't work.
I then compiled one of the many examples of outb (that use inline asm in C) with GCC and I looked at the resulting code, it produces this:
Code: Select all
out_port_byte:
pushl %ebp
movl %esp, %ebp
movzwl 8(%ebp), %edx
movzbl 12(%ebp), %eax
outb %al, %dx
popl %ebp
ret
Now that does work. However, I can't understand why my code doesn't work and that this code does. Can anyone explain?
Cheers,
Puff
Re: Outb; can you explain why one function works and one doesn't
Posted: Thu Dec 18, 2008 6:35 pm
by pcmattman
Yours doesn't work because your offsets are wrong:
Code: Select all
movw 6(%ebp), %dx
movb 7(%ebp), %al
Compared to:
Code: Select all
movzwl 8(%ebp), %edx
movzbl 12(%ebp), %eax
Re: Outb; can you explain why one function works and one doesn't
Posted: Thu Dec 18, 2008 6:43 pm
by CodeCat
In a 32 bit environment all elements on the stack are 32 bits, or 4 bytes in size. No exceptions. You're simply reading the command line arguments from the wrong addresses.
Re: Outb; can you explain why one function works and one doesn't
Posted: Thu Dec 18, 2008 6:45 pm
by Puffy
Edit: Wrote this before the second reply. Cheers guys.
Ok, so if I change my offsets to 8 and 12 it works, but I don't understand why. The function is passed a short (2 bytes) and a char (1 byte), so why do I have to offset by 4 bytes each time?
Re: Outb; can you explain why one function works and one doesn't
Posted: Thu Dec 18, 2008 6:51 pm
by CodeCat
4-byte alignment shows up in more places than just the stack, too. If you declare a struct like this
Code: Select all
struct Blah
{
short one;
int two;
char three;
};
Then on most 32 bit platforms, the size of the entire struct will be 12 bytes. What happens is that because the int needs 4 bytes, it automatically aligns it on a 4-byte boundary (this is faster). So it adds 2 more bytes of empty padding after the 2-byte short. The size of the entire struct is also padded up to the next 4 bytes, which adds another 3 empty bytes after the char. So the real struct is laid out like this:
Code: Select all
struct Blah
{
short one;
char padding1[2];
int two;
char three;
char padding2[3];
};
The general rule with respect to any kind of value is that its first byte must always be located on an address that is a multiple of its own size. So if you have a struct with 2 shorts it will be 4 bytes and not 8, and 4 chars will also be 4 bytes and not 16. But 2 chars in a struct will also make it 4 bytes, because the size of the struct itself is always a multiple of 4.
To take advantage of this, if you ever use values that are less than 4 bytes in size, you should try to always use them in pairs or quads, so that they fill up the space most efficiently without padding.