I highly suggest reading the FAQ lists first, faq102.zip and faq202b.zip, and the online docs, inside the txi*.zip package. There is also a newsgroup, comp.os.msdos.djgpp. The main site is located at DJGPP Homepage, where the most up-to-date information is available on DJGPP and where the mail archives are kept. I find many helpful articles in the mail archives.
movw %bx, %ax (mov ax, bx) xorl %eax, %eax (xor eax, eax) movw $1, %ax (mov ax,1) movb X, %ah (mov ah, byte ptr X) movw X, %ax (mov ax, word ptr X) movl X, %eax (mov eax, X)Most opcodes are identical between AT&T and Intel format, except for these:
movsSD (movsx) movzSD (movzx)Where S and D are the source and destination operand size suffixes, respectively. For example, "movswl %ax, %ecx (movsx ecx, ax)".
cbtw (cbw) cwtl (cwde) cwtd (cwd) cltd (cdq) lcall $S,$O (call far S:O) ljmp $S,$O (jump far S:O) lret $V (ret far V)
Memory references are a little bit different too. The usual Intel memory reference of the form
SECTION:[BASE + INDEX*SCALE + DISP]
is written as
SECTION:DISP(BASE, INDEX, SCALE).
Here are some examples: (Intel equivalent in parentheses)
movl 4(%ebp), %eax (mov eax, [ebp+4]) addl (%eax,%eax,4), %ecx (add ecx, [eax + eax*4]) movb $4, %fs:(%eax) (mov fs:eax, 4) movl _array(,%eax,4), %eax (mov eax, [4*eax + array]) movw _array(%ebx,%eax,4), %cx (mov cx, [ebx + 4*eax + array])Jump instructions always use the smallest displacements. The following instructions always work in byte displacements only, however: "jcxz", "jecxz", "loop", "loopz", "loope", "loopnz" and "loopne". As suggested in the online docs, a "jcxz foo" could be expanded to work:
jcxz cx_zero jmp cx_nonzero cx_zero: jmp foo cx_nonzero:
__asm__(asm statements : outputs : inputs : registers-modified);
The four fields are:
__asm__(" pushl %eax\n movl $1, %eax\n popl %eax" );
Let's spice it up with input variables.
int i = 0; __asm__(" pushl %%eax\n movl %0, %%eax\n addl $1, %%eax\n movl %%eax, %0\n popl %%eax" : : "g" (i) ); /* i++; */Don't panic yet! =) I'll try to explain first. Our input variable is "i" and we want to increment it by 1. We don't have any output variables, nor clobbered registers (we save "eax" ourselves). Therefore the second and last fields are empty.
Since the input field is specified, we need to leave a blank colon for the output field, but none for the last field, since it isn't used. Leave a newline or at least one space between two blank colons.
Let's move on to the input field. Constraints are just instructions you give to the compiler to handle the variables they act upon. They are enclosed in double quotes. So what does the '"g"' mean? '"g"' lets the compiler decide where to load the value of "i" into, as long as the asm instructions accept it. In general, most of your input variables can be constrained as '"g"', letting the compiler decide how to load them (gcc might even optimize it!). Other commonly used constraints are "r" (load into any available register), "a" (ax/eax), "b" (bx/ebx), "c" (cx/ecx), "d" (dx/edx), "D" (di/edi), "S" (si/esi), etc.
The one input we have will be referred to as "%0" inside the asm statements. If we have two inputs, they will be "%0" and "%1", in the order listed in the input fields (see next example). For N inputs and NO outputs, "%0" through "%N-1" will correspond to the inputs, in the order they are listed.
If ANY of the input, output, or registers-modified fields are used, register names inside the asm statements must be preceded with two percent ("%") characters, instead of one. Constrast this example with the first one, which didn't use any of the last three fields.
Let's do two inputs and introduce "volatile":
int i=0, j=1; __asm__ __volatile__(" pushl %%eax\n movl %0, %%eax\n addl %1, %%eax\n movl %%eax, %0\n popl %%eax" : : "g" (i), "g" (j) ); /* i = i + j; */Okay, this time around we have two inputs. No problem, the only thing we have to remember is "%0" corresponds to the first input ("i" in this case), and "%1" to "j", which is listed after "i".
Oh yeah, what exactly is this "volatile" thing? It just prevents the compiler from modifying your asm statements (reordering, deleting, combining, etc.), and assemble them as they are (yes, gcc will optimize if it feels like it!). I suggest using "volatile" most of the time, and from here on we'll be using it.
Let's do one which uses the output field this time.
int i=0; __asm__ __volatile__(" pushl %%eax\n movl $1, %%eax\n movl %%eax, %0\n popl %%eax" : "=g" (i) ); /* i=1; */This looks almost exactly like one of our previous input field examples; and it is really not very different. All output constraints are preceded by an equal ("=") character. They are also referred from "%0" to "%N-1" inside the asm statements, in the order they are listed in the output field. You might ask what happens if one uses both the input and output fields? Well, the next example will show you how to use them together.
int i=0, j=1, k=0; __asm__ __volatile__(" pushl %%eax\n movl %1, %%eax\n addl %2, %%eax\n movl %%eax, %0\n popl %%eax" : "=g" (k) : "g" (i), "g" (j) ); /* k = i + j; */Okay, the only unclear part is just the numbering of the variables inside the asm statements. I'll explain.
When using both input and output fields:
%0 ... %K are the outputs
%K+1 ... %N are the inputs
In our example, "%0" refers to "k", "%1" to "i", and "%2" to "j". Simple, no?
So far we haven't used the last field, registers-modified, at all. If we need to use any register inside our asm statements, we either have to "push" or "pop" them explicitly, or list them in this field and let gcc take care of that. Here's the previous example, without the explicit "eax" save and restore.
int i=0, j=1, k=0; __asm__ __volatile__(" movl %1, %%eax\n addl %2, %%eax\n movl %%eax, %0" : "=g" (k) : "g" (i), "g" (j) : "ax", "memory" ); /* k = i + j; */We tell gcc that we're using register "eax" in the registers-modified field and it will take care of saving or restoring, if necessary. A 16-bit register name covers 32-, 16- or 8-bit registers.
If we are also touching memory (writing to vars, etc.), it's recommended to specify '"memory"' in the registers-modified field. This means all our examples here should have had this specified (well, except the very first one), but I chose not to bring this up until now, just for simplicity.
Local labels inside your inline asm should be terminated with either "b" or "f", for backward and forward references, respectively.
For example,
__asm__ __volatile__(" 0:\n ... jmp 0b\n ... jmp 1f\n ... 1:\n ... );
.file "myasm.S" .data somedata: .word 0 ... .text .globl __myasmfunc __myasmfunc: ... retMacros, macros! There's an include file in
#include <libc/asmdefs.h> .file "myasm.S" .data .align 2 somedata: .word 0 ... .text .align 4 FUNC(__MyExternalAsmFunc) ENTER movl ARG1, %eax ... jmp mylabel ... mylabel: ... LEAVEThat should be a good skeleton for your external asm code.
If you have asm code that needs to be converted from Intel to AT&T syntax, or just want to stick with regular Intel syntax, you can:
GREETS IdealNet & EFNET IRC #c and #gamecode
Thanks to all people behind DJGPP!
Mail me corrections/suggestions/complaints/crap at avly@castle.net
Copyright © 1996 avly@castle.net All Rights Reserved
All trademarks mentioned are of their respective companies.