Assembly programming is a bore, but for critical parts of programs.
You should use the appropriate tool for the right task, so don't choose assembly when it's not fit; C, OCAML, perl, Scheme, might be a better choice for most of your programming.
However, there are cases when these tools do not give a fine enough control on the machine, and assembly is useful or needed. In those case, you'll appreciate a system of macroprocessing and metaprogramming that'll allow recurring patterns to be factored each into a one indefinitely reusable definition, which allows safer programming, automatic propagation of pattern modification, etc. A ``plain'' assembler is often not enough, even when one is doing only small routines to link with C.
Yes I know this section does not contain much useful up-to-date information. Feel free to contribute what you discover the hard way...
GCC allows (and requires) you to specify register constraints in your ``inline assembly'' code, so the optimizer always know about it; thus, inline assembly code is really made of patterns, not forcibly exact code.
Then, you can make put your assembly into CPP macros,
and inline C functions,
so anyone can use it in as any C function/macro.
Inline functions resemble macros very much, but are sometimes cleaner to use.
Beware that in all those cases, code will be duplicated,
so only local labels (of
1: style) should be defined in that asm code.
However, a macro would allow the name for a non local defined label
to be passed as a parameter (or else, you should use additional
Also, note that propagating inline asm code will spread potential bugs in them,
so watch out doubly for register constraints in such inline asm code.
Lastly, the C language itself may be considered as a good abstraction to assembly programming, which relieves you from most of the trouble of assembling.
Beware that some optimizations that involve passing arguments to functions through registers may make those functions unsuitable to be called from external (and particularly hand-written assembly) routines in the standard way; the "asmlinkage" attribute may prevent a routine to be concerned by such optimization flag; see the linux kernel sources for examples.
GAS has some macro capability included, as detailed in the texinfo docs. Moreover, while GCC recognizes .s files as raw assembly to send to GAS, it also recognizes .S files as files to pipe through CPP before to feed them to GAS. Again and again, see Linux sources for examples.
It adds all the usual macroassembly tricks to GAS. See its texinfo docs.
NASM has some macro support, too. See according docs. If you have some bright idea, you might wanna contact the authors, as they are actively developing it. Meanwhile, see about external filters below.
It has some simple macro support, but I couldn't find docs. Now the sources are very straightforward, so if you're interested, you should understand them easily. If you need more than the basics, you should use an external filter (see below).
Whatever is the macro support from your assembler, or whatever language you use (even C !), if the language is not expressive enough to you, you can have files passed through an external filter with a Makefile rule like that:
%.s: %.S other_dependencies $(FILTER) $(FILTER_OPTIONS) < $< > $@
CPP is truely not very expressive, but it's enough for easy things, it's standard, and called transparently by GCC.
As an example of its limitations, you can't declare objects so that destructors are automatically called at the end of the declaring block; you don't have diversions or scoping, etc.
CPP comes with any C compiler. If you could make it without one, don't bother fetching CPP (though I wonder how you could).
M4 gives you the full power of macroprocessing, with a Turing equivalent language, recursion, regular expressions, etc. You can do with it everything that CPP cannot.
See macro4th/This4th from ftp://ftp.forth.org/pub/Forth/ in Reviewed/ ANS/ (?), or the Tunes 0.0.0.25 sources as examples of advanced macroprogramming using m4.
However, its disfunctional quoting and unquoting semantics force you to use explicit continuation-passing tail-recursive macro style if you want to do advanced macro programming (which is remindful of TeX -- BTW, has anyone tried to use TeX as a macroprocessor for anything else than typesetting ?). This is NOT worse than CPP that does not allow quoting and recursion anyway.
The right version of m4 to get is GNU m4 1.4 (or later if exists), which has the most features and the least bugs or limitations of all. m4 is designed to be slow for anything but the simplest uses, which might still be ok for most assembly programming (you're not writing million-lines assembly programs, are you?).
You can write your own simple macro-expansion filter with the usual tools: perl, awk, sed, etc. That's quick to do, and you control everything. But of course, any power in macroprocessing must be earned the hard way.
Instead of using an external filter that expands macros, one way to do things is to write programs that write part or all of other programs.
For instance, you could use a program outputing source code
Think about it!
Compilers like SML/NJ, Objective CAML, MIT-Scheme, etc, do have their own generic assembler backend, which you might or not want to use, if you intend to generate code semi-automatically from the according languages.
There is a project, using the programming language Icon, to build a basis for producing assembly-manipulating code. See around http://www.cs.virginia.edu/~nr/toolkit/
The Tunes OS project is developping its own assembler as an extension to the Scheme language, as part of its development process. It doesn't run at all yet, though help is welcome.
The assembler manipulates symbolic syntax trees, so it could equally serve as the basis for a assembly syntax translator, a disassembler, a common assembler/compiler back-end, etc. Also, the full power of a real language, Scheme, make it unchallenged as for macroprocessing/metaprograming.