Previous Next Contents

3. ASSEMBLERS

3.1 GCC Inline Assembly

The well-known GNU C/C++ Compiler (GCC), an optimizing 32-bit compiler at the heart of the GNU project, supports the x86 architecture quite well, and includes the ability to insert assembly code in C programs, in such a way that register allocation can be either specified or left to GCC. GCC works on most available platforms, notably Linux, *BSD, VSTa, OS/2, *DOS, Win*, etc.

Where to find GCC

The original GCC site is

ftp://prep.ai.mit.edu/pub/gnu/

together with all the released application software from the GNU project. However, there exists a lot of mirrors.

However, sources adapted to your favorite OS, and binaries precompiled for it, should be found at your usual FTP sites.

For GCC under Linux, see around

http://www.linux.org.uk/

For most popular DOS port of GCC is named DJGPP, and can be found in directories of such name in FTP sites. See:

http://www.delorie.com/djgpp/

There is also a port of GCC to OS/2 named EMX, that also works under DOS, and includes lots of unix-emulation library routines. See around:

http://www.leo.org/pub/comp/os/os2/gnu/emx+gcc/

http://warp.eecs.berkeley.edu/os2/software/shareware/emx.html

ftp://ftp-os2.cdrom.com/pub/os2/emx09c/

Where to find docs for GCC Inline Asm

The documentation of GCC includes documentation files in texinfo format, that you can convert to tex, compile (with tex), and print, convert to interactive emacs .info format and browse, convert (with the right tools) to whatever you like, or just read as is. The .info files are generally found on any good installation for GCC.

The right section to look for is: C Extensions::Extended Asm::

Section Invoking GCC::Submodel Options::i386 Options:: might help too. Particularly, it gives the i386 specific constraint names for registers: abcdSDB correspond to %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp respectively (no letter for %esp).

A URL for this document and section, as converted in HTML format, is

http://www.cygnus.com/doc/usegcc_89.html#SEC92

The DJGPP Games resource (not only for game hackers) has this page specifically about assembly:

http://www.rt66.com/~brennan/djgpp/djgpp_asm.html

Finally, there is a web page called, ``DJGPP Quick ASM Programming Guide'', that covers URLs to FAQs, AT&T x86 ASM Syntax, Some inline ASM information, and converting .obj/.lib files:

http://remus.rutgers.edu/~avly/djasm.html

GCC depends on GAS for assembling, and follow its syntax (see below); do mind that inline asm needs percent characters to be quoted so they be passed to GAS. See the section about GAS below.

Find lots of useful examples in the linux/include/asm-i386/ subdirectory of the sources for the free Linux OS.

Invoking GCC to have it properly inline assembly code ?

Be sure to invoke GCC with the -O flag (or -O2, -O3, etc), to enable optimizations and inline assembly. If you don't, your code may compile, but not run properly!!! Actually (kudos to Tim Potter, timbo@moshpit.air.net.au), it is enough to use the -fasm flag, which is part of all the features enabled by

More generally, good compile flags for GCC on the x86 platform are


        gcc -O2 -fomit-frame-pointer -m386

-O2 is the good optimization level. Optimizing besides it yields code that is a lot larger, but only a bit faster; such overoptimizationn might be useful for tight loops only (if any), which you may be doing in assembly anyway; if you need that, do it just for the few routines that need it.

-fomit-frame-pointer allows generated code to skip the stupid frame pointer maintenance, which makes code smaller and faster, and frees a register for further optimizations. It precludes the easy use of debugging tools (gdb), but when you use these, you just don't care about size and speed anymore anyway.

-m386 yields more compact code, without any measurable slowdown, (note that small code also means less disk I/O and faster execution) but perhaps on the above-mentioned tight loops; you might appreciate -mpentium for special pentium-optimizing GCC targetting a specifically pentium platform.

To optimize even more, option -mregparm=2 and/or corresponding function attribute might help, but might pose lots of problems when linking to foreign code...

Note that you can add make these flags the default by editing file /usr/lib/gcc-lib/i486-linux/2.7.2.1/specs or wherever that is on your system.

3.2 GAS

GAS is the GNU Assembler, that GCC relies upon, with

Where to find it

Find it at the same place where you found GCC, in a package named binutils.

What is this AT&T syntax

Because GAS was invented to support a 32-bit unix compiler, it uses standard ``AT&T'' syntax, which resembles a lot the syntax for standard m68k assemblers. This syntax is no worse, no better than the ``Intel'' syntax. It's just different. When you get used to it, you find it much more regular than the Intel syntax, though a bit boring.

A program exists to help you convert programs from TASM syntax to AT&T syntax. See

ftp://x2ftp.oulu.fi/pub/msdos/programming/convert/ta2asv08.zip

A file gas.doc or as.doc (still around the same place as you found GAS, if not in the GAS source package itself) describes the syntax. Of course, the ultimate documentation is the sources themselves!

One place for it is in FTP directory

ftp://sunsite.unc.edu/pub/Linux/GCC/

ftp://sunsite.doc.ic.ac.uk/packages/linux/sunsite.unc-mirror/GCC/ (?)

Again, the sources for Linux (the OS kernel), come in as good examples; see under linux/arch/i386, the following files: kernel/entry.S, kernel/head.S, boot/compressed/head.S, mathemu/*.S

If you are writing kind of a language, a thread package, etc you might as well see how other languages (OCaml, gforth, etc), or thread packages (QT, MIT pthreads, LinuxThreads, etc), or whatever, do it.

Finally, just compiling a C program to assembly might show you the syntax for the kind of instructions you want. See section Do you need Assembly? above.

Limited 16-bit mode

GAS is a 32-bit assembler, meant to support a 32-bit compiler. It currently has only limited support for 16-bit mode, which consists in prepending the 32-bit prefixes to instructions, so you write 32-bit code that runs in 16-bit mode on a 32 bit CPU. In both modes, it supports 16-bit register usage, but what is unsupported is 16-bit addressing. Use the directive code16 and code32 to switch between modes. Note that an inline assembly statement asm("code16\n") will allow GCC to produce 32-bit code that'll run in real mode! Feel free to add full 16-bit support if you think you need it, by modifying GAS. A cheaper solution is to define macros (see below) for just the 16-bit mode instructions you need (almost nothing if you use code16 as above, and can safely assume the code will run on a 32-bit capable x86 CPU). To find the proper encoding, you can get inspiration from the sources of 16-bit capable assemblers for the encoding.

3.3 GASP

GASP is the GAS Preprocessor. It adds macros and some nice syntax to GAS.

Where to find GASP

GASP comes together with GAS in the GNU binutils archive.

How it works

I have no idea, but it comes with its own texinfo documentation, so just print them, or browse the .info files... Looks like a regular macro-assembler to me.

3.4 NASM

The Netwide Assembler project is producing yet another assembler, written in C, that should be modular enough to eventually support all known syntaxes and object formats.

Where to find NASM

http://www.dcs.warwick.ac.uk/~jules/nasm1.html

Binary release on your usual sunsite mirror in devel/lang/asm/

What it does

At the time this HOWTO is written, current NASM version is 0.93.

The syntax is very simple (simplified MASM style). Little integrated macroprocessing.

Supported object file formats are bin, aout, coff, elf, as86, (DOS) obj, win32, (their own format) rdf.

NASM can be used as a backend for the free LCC compiler (support files included).

Surely NASM evolves too fast for this HOWTO to be kept up to date. Soon, perhaps even now, you should use NASM instead of AS86, because NASM is supported and AS86 not so much, unless you also use BCC as a 16-bit compiler package, which is out of scope of this 32-bit HOWTO. NASM online support is reportedly quite good.

Note: NASM also comes with a disassembler, NDISASM.

3.5 AS86

AS86 is a 80x86 assembler, both 16-bit and 32-bit, part of Bruce Evans' C Compiler (BCC). It has mostly Intel-syntax, though it differs slightly as for addressing modes.

Where to get AS86

A completely outdated version of AS86 is distributed by HJLu just to compile the Linux kernel, in a package named bin86 (current version 0.3), available in any Linux GCC repository. But I advise no one to use it for anything else but compiling Linux. This version supports only a hacked minix object file format, and has a few bugs in 32-bit mode, so you better keep it only for compiling Linux.

The most recent versions are published together with the FreeBSD distribution. Well, they were: I could not find the sources from distribution 2.1 on :( Hence, I put the sources in

http://www.eleves.ens.fr:8080/home/rideau/files/bcc-95.3.12.src.tgz

Among other things, it supports Linux GNU a.out format, so you can link you code to Linux programs, and/or use the usual tools from the GNU binutil package to manipulate your data. This version can co-exist without any harm with the previous one (see question 2.4.4 below).

BCC from 12 march 1995 and earlier version has a misfeature that makes all segment pushing/popping 16-bit, which is quite annoying when programming in 32-bit mode. A patch is published in the Tunes project

http://www.eleves.ens.fr:8080/home/rideau/Tunes/

subpage files/tgz/tunes.0.0.0.25.src.tgz in unpacked subdirectory LLL/i386/ The patch should also be in available directly from http://www.eleves.ens.fr:8080/home/rideau/files/as86.bcc.patch.gz Bruce Evans accepted this patch, so if there is a more recent version of bcc somewhere someday, the patch should have been included...

Portability note: as86 makes a lot of presomptuous assumptions about type sizes, which prevents it from correctly running on architectures that fail to meet those. Hence, cross compiling, or even compiling from DOS, might be problematic, and will require patching some files (particularly see ld/typeconv.c about that problem). It should work on all 32-bit unixish architectures, as well as minix and Linux/ELKS.

Send patches to Bruce Evans (bde@zeta.org.au) and me (rideau@ens.fr)

Note for DOS users (): bcc is known to have been successfully used under DOS. I personally tried the following:

How to invoke the assembler?

Here's the GNU Makefile entry for using bcc to transform .s asm into both GNU a.out .o object and .l listing:


%.o %.l:        %.s
        bcc -3 -G -c -A-d -A-l -A$*.l -o $*.o $<

Remove the %.l, -A-l, and -A$*.l, if you don't want any listing. If you want something else than GNU a.out, you can see the docs of bcc about the other supported formats, and/or use the objcopy utility from the GNU binutils package.

Where to find docs

The docs are what is included in the bcc package. Man pages are also available somewhere on the FreeBSD site. When in doubt, the sources themselves are often a good docs: it's not very well commented, but the programming style is clear. You might try to see how as86 is used in Tunes 0.0.0.25...

What if I can't compile Linux anymore with this new version ?

Linus is buried alive in mail, and my patch for compiling Linux with a Linux a.out as86 didn't make it to him (!). Now, this shouldn't matter: just keep your as86 from the bin86 package in /usr/bin, and put the good as86 as /usr/local/libexec/i386/bcc/as where it should be. You never need explicitly call this ``good'' as86, because bcc does everything right, including conversion to Linux a.out, when invoked with the right options; so assemble files exclusively with bcc as a frontend, not directly with as86.

3.6 OTHER ASSEMBLERS

These are other, non-regular, options, in case the previous didn't satisfy you (why ?), that I don't recommend in the usual (?) case, but that could be useful if the assembler is part of what you're designing (i.e. an OS or development environment).

Win32Forth assembler

Win32Forth is a free 32-bit FORTH system that successfully runs under Win32s, Win95, Win/NT. It includes a free 32-bit assembler (either prefix or postfix syntax) integrated to the assembler. Macro processing is done with the full power of the reflective language FORTH; however, the only supported input and output contexts is Win32For itself (no dumping of .obj file -- you could add that yourself, of course). Find it at ftp://ftp.forth.org/pub/Forth/win32for/

Terse

Terse is a programming tool that provides THE most compact assembler syntax for the intel x86 family! See http://www.terse.com

Non-free and/or Non-32bit x86 assemblers.

You may find more about them, together with the basics of x86 assembly programming, in Raymond Moon's FAQ for comp.lang.asm.x86 http://www2.dgsys.com/~raymoon/faq/asmfaq.zip

Note that all DOS-based assemblers should work inside the Linux DOS Emulator, as well as other similar emulators, so that if you already own one, you can still use it inside a real OS. Recent DOS-based assemblers also support COFF and/or other object file formats that are supported by the GNU BFD library, so that you can use them together with your free 32-bit tools, perhaps using GNU objcopy (part of the binutils) as a conversion filter.


Previous Next Contents