810 lines
16 KiB
HTML
810 lines
16 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<HTML
|
|
><HEAD
|
|
><TITLE
|
|
>Linux</TITLE
|
|
><META
|
|
NAME="GENERATOR"
|
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
|
|
REL="HOME"
|
|
TITLE="Linux Assembly HOWTO"
|
|
HREF="index.html"><LINK
|
|
REL="UP"
|
|
TITLE="Calling conventions"
|
|
HREF="conventions.html"><LINK
|
|
REL="PREVIOUS"
|
|
TITLE="Calling conventions"
|
|
HREF="conventions.html"><LINK
|
|
REL="NEXT"
|
|
TITLE="DOS and Windows"
|
|
HREF="dos.html"></HEAD
|
|
><BODY
|
|
CLASS="section"
|
|
BGCOLOR="#FFFFFF"
|
|
TEXT="#000000"
|
|
LINK="#0000FF"
|
|
VLINK="#840084"
|
|
ALINK="#0000FF"
|
|
><DIV
|
|
CLASS="NAVHEADER"
|
|
><TABLE
|
|
SUMMARY="Header navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TH
|
|
COLSPAN="3"
|
|
ALIGN="center"
|
|
>Linux Assembly HOWTO</TH
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="left"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="conventions.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="80%"
|
|
ALIGN="center"
|
|
VALIGN="bottom"
|
|
>Chapter 5. Calling conventions</TD
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="right"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="dos.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"></DIV
|
|
><DIV
|
|
CLASS="section"
|
|
><H1
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN632"
|
|
></A
|
|
>5.1. Linux</H1
|
|
><DIV
|
|
CLASS="section"
|
|
><H2
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN634"
|
|
></A
|
|
>5.1.1. Linking to GCC</H2
|
|
><P
|
|
> This is the preferred way if you are developing mixed C-asm project. Check GCC
|
|
docs and examples from Linux kernel <TT
|
|
CLASS="filename"
|
|
>.S</TT
|
|
> files that go through
|
|
<SPAN
|
|
CLASS="application"
|
|
>gas</SPAN
|
|
> (not those that go through
|
|
<SPAN
|
|
CLASS="application"
|
|
>as86</SPAN
|
|
>).
|
|
</P
|
|
><P
|
|
> 32-bit arguments are pushed down stack in reverse syntactic order (hence
|
|
accessed/popped in the right order), above the 32-bit near return address.
|
|
<TT
|
|
CLASS="literal"
|
|
>%ebp</TT
|
|
>, <TT
|
|
CLASS="literal"
|
|
>%esi</TT
|
|
>, <TT
|
|
CLASS="literal"
|
|
>%edi</TT
|
|
>,
|
|
<TT
|
|
CLASS="literal"
|
|
>%ebx</TT
|
|
> are callee-saved, other registers are caller-saved;
|
|
<TT
|
|
CLASS="literal"
|
|
>%eax</TT
|
|
> is to hold the result, or <TT
|
|
CLASS="literal"
|
|
>%edx:%eax</TT
|
|
>
|
|
for 64-bit results.
|
|
</P
|
|
><P
|
|
> FP stack: I'm not sure, but I think result is in <TT
|
|
CLASS="literal"
|
|
>st(0)</TT
|
|
>,
|
|
whole stack caller-saved.
|
|
</P
|
|
><P
|
|
> Note that GCC has options to modify the calling conventions by reserving
|
|
registers, having arguments in registers, not assuming the FPU, etc. Check the
|
|
i386 <TT
|
|
CLASS="filename"
|
|
>.info</TT
|
|
> pages.
|
|
</P
|
|
><P
|
|
> Beware that you must then declare the <TT
|
|
CLASS="literal"
|
|
>cdecl</TT
|
|
> or
|
|
<TT
|
|
CLASS="literal"
|
|
>regparm(0)</TT
|
|
> attribute for a function that will follow
|
|
standard GCC calling conventions. See
|
|
<TT
|
|
CLASS="literal"
|
|
>C Extensions::Extended Asm::</TT
|
|
> section from the GCC info
|
|
pages. See also how Linux defines its <TT
|
|
CLASS="literal"
|
|
>asmlinkage</TT
|
|
> macro.
|
|
</P
|
|
></DIV
|
|
><DIV
|
|
CLASS="section"
|
|
><H2
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN656"
|
|
></A
|
|
>5.1.2. ELF vs a.out problems</H2
|
|
><P
|
|
> Some C compilers prepend an underscore before every symbol, while others do
|
|
not.
|
|
</P
|
|
><P
|
|
> Particularly, Linux a.out GCC does such prepending, while Linux ELF GCC does
|
|
not.
|
|
</P
|
|
><P
|
|
> If you need to cope with both behaviors at once, see how existing packages do.
|
|
For instance, get an old Linux source tree, the Elk, qthreads, or OCaml.
|
|
</P
|
|
><P
|
|
> You can also override the implicit C->asm renaming by inserting statements like
|
|
|
|
<TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><FONT
|
|
COLOR="#000000"
|
|
><PRE
|
|
CLASS="programlisting"
|
|
> void foo asm("bar") (void);
|
|
</PRE
|
|
></FONT
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
>
|
|
|
|
to be sure that the C function <TT
|
|
CLASS="function"
|
|
>foo()</TT
|
|
>
|
|
will be called really <TT
|
|
CLASS="function"
|
|
>bar</TT
|
|
> in assembly.
|
|
</P
|
|
><P
|
|
> Note that the <B
|
|
CLASS="command"
|
|
>objcopy</B
|
|
> utility from the binutils package
|
|
should allow you to transform your a.out objects into ELF objects,
|
|
and perhaps the contrary too, in some cases.
|
|
More generally, it will do lots of file format conversions.
|
|
</P
|
|
></DIV
|
|
><DIV
|
|
CLASS="section"
|
|
><H2
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN667"
|
|
></A
|
|
>5.1.3. Direct Linux syscalls</H2
|
|
><P
|
|
> Often you will be told that using <SPAN
|
|
CLASS="application"
|
|
>C library</SPAN
|
|
>
|
|
(<SPAN
|
|
CLASS="acronym"
|
|
>libc</SPAN
|
|
>) is the only way, and direct system calls are bad.
|
|
This is true. To some extent. In general, you must know that
|
|
<SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
> is not sacred, and in <EM
|
|
>most</EM
|
|
>
|
|
cases it only does some checks, then calls kernel, and then sets errno. You can
|
|
easily do this in your program as well (if you need to), and your program will
|
|
be dozen times smaller, and this will result in improved performance as well,
|
|
just because you're not using shared libraries (static binaries are faster).
|
|
Using or not using <SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
> in assembly programming is
|
|
more a question of taste/belief than something practical. Remember, Linux is
|
|
aiming to be POSIX compliant, so does <SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
>.
|
|
This means that syntax of almost all <SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
> "system
|
|
calls" exactly matches syntax of real kernel system calls (and vice versa).
|
|
Besides, <SPAN
|
|
CLASS="application"
|
|
>GNU libc</SPAN
|
|
>(<SPAN
|
|
CLASS="application"
|
|
>glibc</SPAN
|
|
>)
|
|
becomes slower and slower from version to version, and eats more and more
|
|
memory; and so, cases of using direct system calls become quite usual.
|
|
However, the main drawback of throwing <SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
> away
|
|
is that you will possibly need to implement several
|
|
<SPAN
|
|
CLASS="application"
|
|
>libc</SPAN
|
|
> specific functions (that are not just syscall
|
|
wrappers) on your own (<TT
|
|
CLASS="function"
|
|
>printf()</TT
|
|
> and Co.), and you are
|
|
ready for that, aren't you? :-)
|
|
</P
|
|
><P
|
|
> Here is summary of direct system calls pros and cons.
|
|
</P
|
|
><P
|
|
> Pros:
|
|
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
> the smallest possible size; squeezing the last byte out of the system
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> the highest possible speed; squeezing cycles out of your favorite benchmark
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> full control: you can adapt your program/library to your specific language or
|
|
memory requirements or whatever
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> no pollution by libc cruft
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> no pollution by C calling conventions (if you're developing your own language
|
|
or environment)
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> static binaries make you independent from libc upgrades or crashes, or from
|
|
dangling <TT
|
|
CLASS="literal"
|
|
>#!</TT
|
|
> path to an interpreter (and are faster)
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> just for the fun out of it (don't you get a kick out of assembly programming?)
|
|
</P
|
|
></LI
|
|
></UL
|
|
>
|
|
</P
|
|
><P
|
|
> Cons:
|
|
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
> If any other program on your computer uses the libc, then duplicating the libc
|
|
code will actually wastes memory, not saves it.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Services redundantly implemented in many static binaries are a waste of memory.
|
|
But you can make your libc replacement a shared library.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Size is much better saved by having some kind of bytecode, wordcode, or
|
|
structure interpreter than by writing everything in assembly. (the interpreter
|
|
itself could be written either in C or assembly.) The best way to keep multiple
|
|
binaries small is to not have multiple binaries, but instead to have an
|
|
interpreter process files with <TT
|
|
CLASS="literal"
|
|
>#!</TT
|
|
> prefix. This is how OCaml
|
|
works when used in wordcode mode (as opposed to optimized native code mode),
|
|
and it is compatible with using the libc. This is also how Tom Christiansen's
|
|
Perl PowerTools reimplementation of unix utilities works. Finally, one last way
|
|
to keep things small, that doesn't depend on an external file with a hardcoded
|
|
path, be it library or interpreter, is to have only one binary, and have
|
|
multiply-named hard or soft links to it: the same binary will provide everything
|
|
you need in an optimal space, with no redundancy of subroutines or useless
|
|
binary headers; it will dispatch its specific behavior according to its
|
|
<TT
|
|
CLASS="parameter"
|
|
><I
|
|
>argv[0]</I
|
|
></TT
|
|
>; in case it isn't called with a recognized name,
|
|
it might default to a shell, and be possibly thus also usable as an interpreter!
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> You cannot benefit from the many functionalities that libc provides besides mere
|
|
linux syscalls: that is, functionality described in section 3 of the manual
|
|
pages, as opposed to section 2, such as malloc, threads, locale, password,
|
|
high-level network management, etc.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Therefore, you might have to reimplement large parts of libc, from
|
|
<TT
|
|
CLASS="function"
|
|
>printf()</TT
|
|
> to <TT
|
|
CLASS="function"
|
|
>malloc()</TT
|
|
> and
|
|
<TT
|
|
CLASS="function"
|
|
>gethostbyname</TT
|
|
>. It's redundant with the libc effort, and
|
|
can be <EM
|
|
>quite</EM
|
|
> boring sometimes. Note that some people have
|
|
already reimplemented "light" replacements for parts of the libc - - check
|
|
them out! (Redhat's minilibc, Rick Hohensee's
|
|
libsys,
|
|
Felix von Leitner's dietlibc,
|
|
|
|
asmutils
|
|
project is working on pure assembly libc)
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Static libraries prevent you to benefit from libc upgrades as well as from libc
|
|
add-ons such as the <SPAN
|
|
CLASS="application"
|
|
>zlibc</SPAN
|
|
> package, that does
|
|
on-the-fly transparent decompression of gzip-compressed files.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> The few instructions added by the libc can be a
|
|
<EM
|
|
>ridiculously</EM
|
|
> small speed overhead as compared to the cost
|
|
of a system call. If speed is a concern, your main problem is in your usage of
|
|
system calls, not in their wrapper's implementation.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Using the standard assembly API for system calls is much slower than using the
|
|
libc API when running in micro-kernel versions of Linux such as L4Linux, that
|
|
have their own faster calling convention, and pay high convention-translation
|
|
overhead when using the standard one (L4Linux comes with libc recompiled with
|
|
their syscall API; of course, you could recompile your code with their API,
|
|
too).
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> See previous discussion for general speed optimization issue.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> If syscalls are too slow to you, you might want to hack the kernel sources
|
|
(in C) instead of staying in userland.
|
|
</P
|
|
></LI
|
|
></UL
|
|
>
|
|
</P
|
|
><P
|
|
> If you've pondered the above pros and cons, and still want to use direct
|
|
syscalls, then here is some advice.
|
|
</P
|
|
><P
|
|
> <P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
> You can easily define your system calling functions in a portable way in C
|
|
(as opposed to unportable using assembly), by including
|
|
<TT
|
|
CLASS="filename"
|
|
>asm/unistd.h</TT
|
|
>, and using provided macros.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> Since you're trying to replace it, go get the sources for the libc, and
|
|
grok them. (And if you think you can do better, then send feedback to the
|
|
authors!)
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> As an example of pure assembly code that does everything you want, examine
|
|
.
|
|
</P
|
|
></LI
|
|
></UL
|
|
>
|
|
</P
|
|
><P
|
|
> Basically, you issue an <TT
|
|
CLASS="function"
|
|
>int 0x80</TT
|
|
>, with the
|
|
<TT
|
|
CLASS="literal"
|
|
>__NR_</TT
|
|
>syscallname number (from <TT
|
|
CLASS="filename"
|
|
>asm/unistd.h</TT
|
|
>)
|
|
in <TT
|
|
CLASS="literal"
|
|
>eax</TT
|
|
>, and parameters (up to six)
|
|
in <TT
|
|
CLASS="literal"
|
|
>ebx</TT
|
|
>, <TT
|
|
CLASS="literal"
|
|
>ecx</TT
|
|
>, <TT
|
|
CLASS="literal"
|
|
>edx</TT
|
|
>,
|
|
<TT
|
|
CLASS="literal"
|
|
>esi</TT
|
|
>, <TT
|
|
CLASS="literal"
|
|
>edi</TT
|
|
>,
|
|
<TT
|
|
CLASS="literal"
|
|
>ebp</TT
|
|
> respectively.
|
|
</P
|
|
><P
|
|
> Result is returned in <TT
|
|
CLASS="literal"
|
|
>eax</TT
|
|
>, with a negative result being an
|
|
error, whose opposite is what libc would put into <TT
|
|
CLASS="literal"
|
|
>errno</TT
|
|
>.
|
|
The user-stack is not touched, so you needn't have a valid one when doing a
|
|
syscall.
|
|
</P
|
|
><DIV
|
|
CLASS="note"
|
|
><P
|
|
></P
|
|
><TABLE
|
|
CLASS="note"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="25"
|
|
ALIGN="CENTER"
|
|
VALIGN="TOP"
|
|
><IMG
|
|
SRC="../images/note.gif"
|
|
HSPACE="5"
|
|
ALT="Note"></TD
|
|
><TD
|
|
ALIGN="LEFT"
|
|
VALIGN="TOP"
|
|
><P
|
|
> <A
|
|
NAME=""
|
|
></A
|
|
>
|
|
Passing sixth parameter in <TT
|
|
CLASS="literal"
|
|
>ebp</TT
|
|
> appeared in Linux 2.4,
|
|
previous Linux versions understand only 5 parameters in registers.
|
|
</P
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><P
|
|
> Linux Kernel Internals,
|
|
and especially
|
|
How System Calls Are Implemented on i386 Architecture? chapter will give
|
|
you more robust overview.
|
|
</P
|
|
><P
|
|
> As for the invocation arguments passed to a process upon startup, the general
|
|
principle is that the stack originally contains the number of arguments
|
|
<TT
|
|
CLASS="parameter"
|
|
><I
|
|
>argc</I
|
|
></TT
|
|
>, then the list of pointers that constitute
|
|
<TT
|
|
CLASS="parameter"
|
|
><I
|
|
>*argv</I
|
|
></TT
|
|
>, then a null-terminated sequence of
|
|
null-terminated <TT
|
|
CLASS="literal"
|
|
>variable=value</TT
|
|
> strings for the
|
|
<TT
|
|
CLASS="parameter"
|
|
><I
|
|
>environ</I
|
|
></TT
|
|
>ment. For more details, do examine
|
|
, read the sources of C startup code from your libc
|
|
(<TT
|
|
CLASS="filename"
|
|
>crt0.S</TT
|
|
> or <TT
|
|
CLASS="filename"
|
|
>crt1.S</TT
|
|
>), or those from
|
|
the Linux kernel (<TT
|
|
CLASS="filename"
|
|
>exec.c</TT
|
|
> and
|
|
<TT
|
|
CLASS="filename"
|
|
>binfmt_*.c</TT
|
|
> in <TT
|
|
CLASS="filename"
|
|
>linux/fs/</TT
|
|
>).
|
|
</P
|
|
></DIV
|
|
><DIV
|
|
CLASS="section"
|
|
><H2
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN778"
|
|
></A
|
|
>5.1.4. Hardware I/O under Linux</H2
|
|
><P
|
|
> If you want to perform direct port I/O under Linux, either it's something very
|
|
simple that does not need OS arbitration, and you should see the
|
|
<TT
|
|
CLASS="literal"
|
|
>IO-Port-Programming</TT
|
|
> mini-HOWTO; or it needs a kernel device
|
|
driver, and you should try to learn more about kernel hacking, device driver
|
|
development, kernel modules, etc, for which there are other excellent HOWTOs
|
|
and documents from the LDP.
|
|
</P
|
|
><P
|
|
> Particularly, if what you want is Graphics programming, then do join one of the
|
|
GGI or
|
|
XFree86 projects.
|
|
</P
|
|
><P
|
|
> Some people have even done better, writing small and robust XFree86 drivers in
|
|
an interpreted domain-specific language, GAL, and achieving the efficiency of
|
|
hand C-written drivers through partial evaluation (drivers not only not in asm,
|
|
but not even in C!). The problem is that the partial evaluator they used to
|
|
achieve efficiency is not free software. Any taker for a replacement?
|
|
</P
|
|
><P
|
|
> Anyway, in all these cases, you'll be better when using GCC inline assembly
|
|
with the macros from <TT
|
|
CLASS="filename"
|
|
>linux/asm/*.h</TT
|
|
> than writing full
|
|
assembly source files.
|
|
</P
|
|
></DIV
|
|
><DIV
|
|
CLASS="section"
|
|
><H2
|
|
CLASS="section"
|
|
><A
|
|
NAME="AEN788"
|
|
></A
|
|
>5.1.5. Accessing 16-bit drivers from Linux/i386</H2
|
|
><P
|
|
> Such thing is theoretically possible (proof: see how
|
|
DOSEMU can selectively grant
|
|
hardware port access to programs), and I've heard rumors that someone somewhere
|
|
did actually do it (in the PCI driver? Some VESA access stuff? ISA PnP? dunno).
|
|
If you have some more precise information on that, you'll be most welcome.
|
|
Anyway, good places to look for more information are the Linux kernel sources,
|
|
DOSEMU sources, and sources for various low-level programs under Linux.
|
|
(perhaps GGI if it supports VESA).
|
|
</P
|
|
><P
|
|
> Basically, you must either use 16-bit protected mode or vm86 mode.
|
|
</P
|
|
><P
|
|
> The first is simpler to setup, but only works with well-behaved code that won't
|
|
do any kind of segment arithmetics or absolute segment addressing (particularly
|
|
addressing segment 0), unless by chance it happens that all segments used can
|
|
be setup in advance in the LDT.
|
|
</P
|
|
><P
|
|
> The later allows for more "compatibility" with vanilla 16-bit environments, but
|
|
requires more complicated handling.
|
|
</P
|
|
><P
|
|
> In both cases, before you can jump to 16-bit code, you must
|
|
|
|
<P
|
|
></P
|
|
><UL
|
|
><LI
|
|
><P
|
|
> mmap any absolute address used in the 16-bit code (such as ROM, video buffers,
|
|
DMA targets, and memory-mapped I/O) from <TT
|
|
CLASS="filename"
|
|
>/dev/mem</TT
|
|
> to your
|
|
process' address space,
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> setup the LDT and/or vm86 mode monitor.
|
|
</P
|
|
></LI
|
|
><LI
|
|
><P
|
|
> grab proper I/O permissions from the kernel (see the above section)
|
|
</P
|
|
></LI
|
|
></UL
|
|
>
|
|
</P
|
|
><P
|
|
> Again, carefully read the source for the stuff contributed to the DOSEMU
|
|
project, particularly these mini-emulators for running ELKS and/or simple
|
|
<TT
|
|
CLASS="filename"
|
|
>.COM</TT
|
|
> programs under Linux/i386.
|
|
</P
|
|
></DIV
|
|
></DIV
|
|
><DIV
|
|
CLASS="NAVFOOTER"
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"><TABLE
|
|
SUMMARY="Footer navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="conventions.html"
|
|
ACCESSKEY="P"
|
|
>Prev</A
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="index.html"
|
|
ACCESSKEY="H"
|
|
>Home</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="dos.html"
|
|
ACCESSKEY="N"
|
|
>Next</A
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
>Calling conventions</TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="conventions.html"
|
|
ACCESSKEY="U"
|
|
>Up</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
>DOS and Windows</TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></BODY
|
|
></HTML
|
|
> |