www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/2000/04/09/04:46:48

From: "Alexei A. Frounze" <alex DOT fru AT mtu-net DOT ru>
Newsgroups: comp.os.msdos.djgpp
Subject: Re: 'retf' in inline?
Date: Sun, 09 Apr 2000 11:21:22 +0400
Organization: MTU-Intel ISP
Lines: 389
Message-ID: <38F02F72.A7C88440@mtu-net.ru>
References: <38EFFFE0 DOT B217D192 AT home DOT com>
NNTP-Posting-Host: ppp98-207.dialup.mtu-net.ru
Mime-Version: 1.0
X-Trace: gavrilo.mtu.ru 955269577 33394 212.188.98.207 (9 Apr 2000 08:39:37 GMT)
X-Complaints-To: usenet-abuse AT mtu DOT ru
NNTP-Posting-Date: 9 Apr 2000 08:39:37 GMT
X-Mailer: Mozilla 4.61 [en] (Win95; I)
X-Accept-Language: en,ru
To: djgpp AT delorie DOT com
DJ-Gateway: from newsgroup comp.os.msdos.djgpp
Reply-To: djgpp AT delorie DOT com

This is a multi-part message in MIME format.
--------------321B74DB222A04F00D51A48A
Content-Type: text/plain; charset=koi8-r
Content-Transfer-Encoding: 7bit

Hi Mark!

I think this will help you...

Regards,
Alexei A. Frounze
-----------------------------------------
Homepage: http://alexfru.chat.ru
Mirror:   http://members.xoom.com/alexfru
--------------321B74DB222A04F00D51A48A
Content-Type: text/plain; charset=koi8-r;
 name="djasm.txt"
Content-Transfer-Encoding: 8bit
Content-Disposition: inline;
 filename="djasm.txt"

                      DJGPP QuickAsm Programming Guide

Okay, so this tutorial has long been overdue, I've been putting it off for
a couple of months; but right now I'm in the mood to write it so here it
is. This is just a short tutorial on doing assembly code using DJGPP. I am
NOT teaching how to code x86 asm (get another tutorial or book); but I'll
try to show how to do both inline and external asm in DJGPP. I assume you
are already familiar with "standard" Intel asm, as used in TASM, MASM, etc.

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.

---------------------------------------------------------------------------

AT&T x86 Asm Syntax

DJGPP uses AT&T asm syntax. This is a little bit different from the regular
Intel format. The main differences are:

   * AT&T syntax uses the opposite order for source and destination
     operands, source followed by destination.
   * Register operands are preceded by the "%" character, including
     sections.
   * Immediate operands are preceded by the "$" character.
   * The size of memory operands are specified using the last character of
     the opcode. These are "b" (8-bit), "w" (16-bit), and "l" (32-bit).

Here are some examples: (Intel equivalent in parentheses)

        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)

 [GOTCHA!] ] Opcode prefixes should NOT be written on the same line as the
instruction they act upon. For example, "rep" and "stosd" should be two
separate instructions, but the latter immediately following the former.

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:

 [GOTCHA!] ] The online docs also caution on "mul" and "imul" instructions.
The expanding multiply instructions are done using ONE operand. For
example, "imul $ebx, $ebx" will NOT put the result in "edx:eax". Use the
single operand form "imul %ebx" to get the expanded result.

---------------------------------------------------------------------------

Inline Asm

I'll start with inline asm first, because it seems to be the more
frequently asked question. This is the basic syntax, as described in the
online help:

__asm__(asm statements : outputs : inputs : registers-modified);

The four fields are:

   * asm statements - AT&T form, separated by newline
   * outputs - constraint followed by name in parentheses, separated by
     comma
   * inputs - constraint followed by name in parentheses, separated by
     comma
   * registers-modified - names separated by comma

The simplest example:

        __asm__("
          pushl %eax\n
          movl $1, %eax\n
          popl %eax"
        );

 [GOTCHA!] ] You don't always have to use the other three fields, as long
as you don't want to specify any input/output variables and you're NOT
accidentally clobbering any registers.

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.

 [GOTCHA!] ] 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.

 [GOTCHA!] ] 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".

 [GOTCHA!] ] 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.

 [GOTCHA!] ] 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.

 [GOTCHA!] ] 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
          ...
        );

---------------------------------------------------------------------------

External Asm

Blah... Okay fine. Here's a clue: Get some of your C/C++ files, and compile
them as "gcc -S file.c" Then inspect file.S. The basic layout is:

  .file "myasm.S"

  .data
    somedata: .word 0
    ...

  .text
    .globl __myasmfunc
    __myasmfunc:
      ...
      ret

Macros, macros! There's an include file in which is convenient for writing
external asm files. Just include it on top of your asm source and use the
macros accordingly. For example, here's myasm.S:

  #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:
      ...
    LEAVE

That should be a good skeleton for your external asm code.

---------------------------------------------------------------------------

Other Resources

The best way to learn all these is to look at others' code. There's some
inline asm code in the "sys/farptr.h" file. Also, if you run Linux,
FreeBSD, etc., somewhere in the kernel source tree (i386/ or something),
there are plenty of asm sources. Check the djgpp2/ dir at x2ftp.oulu.fi,
for graphics/gaming libraries that have sources.

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:

   * Get NASM, a free assembler which takes Intel asm format and produces
     COFF binaries compatible with DJGPP
   * Get MASM and compile your sources to COFF format (object file format
     used by DJGPP)
   * Get ta2asv08.zip, a TASM to AT&T asm converter
   * Get o2cv10.arj to convert .OBJ/.LIB between TASM and DJGPP
   * Search the mail archives for a sed script that converts Intel to AT&T
     syntax

---------------------------------------------------------------------------
1/12/97 icbm AT IRC

GREETS IdealNet & EFNET IRC #c and #gamecode

Thanks to all people behind DJGPP!

Mail me corrections/suggestions/complaints/crap at avly AT castle DOT net

Copyright ¿ 1996 avly AT castle DOT net All Rights Reserved

All trademarks mentioned are of their respective companies.

Standard Disclaimer

There are absolutely no guarantees, expressed or implied, on anything that
you find in this document. I cannot be held responsible for anything that
results from the use or misuse of this document.

--------------321B74DB222A04F00D51A48A--



- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019