Ok. This is meant to be an introduction to inline assembly under DJGPP.
DJGPP is based on GCC, so it uses the AT&T/UNIX syntax and has a
somewhat unique method of inline assembly. I spent many hours figuring
some of this stuff out and told *Info* that I hate it, many times.
Hopefully if you already know Intel syntax, the examples will be helpful
to you. I've put variable names, register names and other literals in
*bold type*.
The Syntax
So, DJGPP uses the AT&T assembly syntax. What does that mean to you?
* *Register naming:*
Register names are prefixed with "%". To reference *eax*:
*AT&T:* %eax
*Intel:* eax
* *Source/Destination Ordering:*
In AT&T syntax (which is the UNIX standard, BTW) the source is
/always/ on the *left*, and the destination is /always/ on the *right*.
So let's load *ebx* with the value in *eax*:
*AT&T:* movl %eax, %ebx
*Intel:* mov ebx, eax
* *Constant value/immediate value format:*
You must prefix all constant/immediate values with "$".
Let's load *eax* with the address of the "C" variable *booga*, which
is static.
*AT&T:* movl $_booga, %eax
*Intel:* mov eax, _booga
Now let's load *ebx* with *0xd00d*:
*AT&T:* movl $0xd00d, %ebx
*Intel:* mov ebx, d00dh
* *Operator size specification:*
You must suffix the instruction with one of *b*, *w*, or *l* to
specify the width of the destination register as a *byte*, *word* or
*longword*. If you omit this, GAS (GNU assembler) will attempt to
guess. /You don't want GAS to guess, and guess wrong!/ Don't forget it.
*AT&T:* movw %ax, %bx
*Intel:* mov bx, ax
The equivalent forms for Intel is *byte ptr*, *word ptr*, and *dword
ptr*, but that is for when you are...
* *Referencing memory:*
DJGPP uses 386-protected mode, so you can forget all that real-mode
addressing junk, including the restrictions on which register has
what default segment, which registers can be base or index pointers.
Now, we just get 6 general purpose registers. (7 if you use *ebp*,
but be sure to restore it yourself or compile with
*-fomit-frame-pointer*.)
Here is the canonical format for 32-bit addressing:
*AT&T:* immed32(basepointer,indexpointer,indexscale)
*Intel:* [basepointer + indexpointer*indexscale + immed32]
You could think of the formula to calculate the address as:
immed32 + basepointer + indexpointer * indexscale
You don't have to use all those fields, but you /do/ have to have at
least 1 of immed32, basepointer and you /MUST/ add the size suffix
to the operator!
Let's see some simple forms of memory addressing:
o Addressing a particular C variable:
*AT&T:* _booga
*Intel:* [_booga]
Note: the underscore ("_") is how you get at static (global) C
variables from assembler. /This only works with global
variables/. Otherwise, you can use extended asm to have
variables preloaded into registers for you. I address that
farther down.
o Addressing what a register points to:
*AT&T:* (%eax)
*Intel:* [eax]
o Addressing a variable offset by a value in a register:
*AT&T:* _variable(%eax)
*Intel:* [eax + _variable]
o Addressing a value in an array of integers (scaling up by 4):
*AT&T:* _array(,%eax,4)
*Intel:* [eax*4 + array]
o You can also do offsets with the immediate value:
*C code:* *(p+1) where p is a char *
*AT&T:* 1(%eax) where eax has the value of p
*Intel:* [eax + 1]
o You can do some simple math on the immediate value:
*AT&T:* _struct_pointer+8
I assume you can do that with Intel format as well.
o Addressing a particular char in an array of 8-character records:
*eax* holds the number of the record desired. *ebx* has the
wanted char's offset within the record.
*AT&T:* _array(%ebx,%eax,8)
*Intel:* [ebx + eax*8 + _array]
Whew. Hopefully that covers all the addressing you'll need to do. As
a note, you can put *esp* into the address, but only as the base
register.
DJGPP is based on GCC, so it uses the AT&T/UNIX syntax and has a
somewhat unique method of inline assembly. I spent many hours figuring
some of this stuff out and told *Info* that I hate it, many times.
Hopefully if you already know Intel syntax, the examples will be helpful
to you. I've put variable names, register names and other literals in
*bold type*.
The Syntax
So, DJGPP uses the AT&T assembly syntax. What does that mean to you?
* *Register naming:*
Register names are prefixed with "%". To reference *eax*:
*AT&T:* %eax
*Intel:* eax
* *Source/Destination Ordering:*
In AT&T syntax (which is the UNIX standard, BTW) the source is
/always/ on the *left*, and the destination is /always/ on the *right*.
So let's load *ebx* with the value in *eax*:
*AT&T:* movl %eax, %ebx
*Intel:* mov ebx, eax
* *Constant value/immediate value format:*
You must prefix all constant/immediate values with "$".
Let's load *eax* with the address of the "C" variable *booga*, which
is static.
*AT&T:* movl $_booga, %eax
*Intel:* mov eax, _booga
Now let's load *ebx* with *0xd00d*:
*AT&T:* movl $0xd00d, %ebx
*Intel:* mov ebx, d00dh
* *Operator size specification:*
You must suffix the instruction with one of *b*, *w*, or *l* to
specify the width of the destination register as a *byte*, *word* or
*longword*. If you omit this, GAS (GNU assembler) will attempt to
guess. /You don't want GAS to guess, and guess wrong!/ Don't forget it.
*AT&T:* movw %ax, %bx
*Intel:* mov bx, ax
The equivalent forms for Intel is *byte ptr*, *word ptr*, and *dword
ptr*, but that is for when you are...
* *Referencing memory:*
DJGPP uses 386-protected mode, so you can forget all that real-mode
addressing junk, including the restrictions on which register has
what default segment, which registers can be base or index pointers.
Now, we just get 6 general purpose registers. (7 if you use *ebp*,
but be sure to restore it yourself or compile with
*-fomit-frame-pointer*.)
Here is the canonical format for 32-bit addressing:
*AT&T:* immed32(basepointer,indexpointer,indexscale)
*Intel:* [basepointer + indexpointer*indexscale + immed32]
You could think of the formula to calculate the address as:
immed32 + basepointer + indexpointer * indexscale
You don't have to use all those fields, but you /do/ have to have at
least 1 of immed32, basepointer and you /MUST/ add the size suffix
to the operator!
Let's see some simple forms of memory addressing:
o Addressing a particular C variable:
*AT&T:* _booga
*Intel:* [_booga]
Note: the underscore ("_") is how you get at static (global) C
variables from assembler. /This only works with global
variables/. Otherwise, you can use extended asm to have
variables preloaded into registers for you. I address that
farther down.
o Addressing what a register points to:
*AT&T:* (%eax)
*Intel:* [eax]
o Addressing a variable offset by a value in a register:
*AT&T:* _variable(%eax)
*Intel:* [eax + _variable]
o Addressing a value in an array of integers (scaling up by 4):
*AT&T:* _array(,%eax,4)
*Intel:* [eax*4 + array]
o You can also do offsets with the immediate value:
*C code:* *(p+1) where p is a char *
*AT&T:* 1(%eax) where eax has the value of p
*Intel:* [eax + 1]
o You can do some simple math on the immediate value:
*AT&T:* _struct_pointer+8
I assume you can do that with Intel format as well.
o Addressing a particular char in an array of 8-character records:
*eax* holds the number of the record desired. *ebx* has the
wanted char's offset within the record.
*AT&T:* _array(%ebx,%eax,8)
*Intel:* [ebx + eax*8 + _array]
Whew. Hopefully that covers all the addressing you'll need to do. As
a note, you can put *esp* into the address, but only as the base
register.