                 ------- Knut's Watcom tutorial #1 -------

                   * Using Watcom's in-line assembler *

GENERAL
-------

The reason why I have written this tutorial, is to make it easier for other
people to start using Watcom. The on-line documentation isn't very
good, and the in-line assembler has a very strange format compared to
other compilers. I had a very hard time learning myself Watcom, and I would 
like to give John S. Price from Interplay a big thanks for helping me
so much. He has also written some parts of this tutorial. 

In this document, I will go through the process of making a function that 
uses in-line asm. Sounds interesting?  Then read on...

A FUNCTION PROTOTYPE
--------------------

All calls to inline assembly in Watcom is done through function calls.
This is not to say that an actual C-type call is generated at run-time;
on the contrary, and as the name implies, all inline assembly in Watcom
is generated and expanded inline at the call site.  We have control, and
can tell the compiler the types, of all the parameters to our inline
assembly function.  

Note: due to the small register set on Intel processors, the maximum
      number of parameters to an inline assembly chunk is 6

Our function will plot a pixel with a specified colour on a standard
320x200 256-colour screen. I will call the function plot, and it will
need the following parameters:

X coordinate for pixel
Y coordinate for pixel
colour of pixel

Looking at the required parameters, the function proto-type will look like
this:

void plot(long X, long Y, long colour);

Now, why have I declared every parameter as long?  Mainly as an optimization.
When Watcom passes parameters to functions, or when it loads smaller-than-
sizeof(long) objects into registers, it has to guarentee that these values
are in the right range.  To do this, Watcom generates a mask to mask the
value into the right range.  This is extra code that we can take care of
ourselves; thus, we use longs to prevent this extra code from being 
generated.


IN-LINE ASSEMBLER
-----------------

As you might have read in the on-line help for Watcom, a basic outline for 
a in-line assembler function looks something like this:

#pragma aux function_name =

<the assembler instructions goes here>

modify [ registers ]
value [ registers ]
parm [ registers ]

The two last lines (value and parm) are not always needed, but it depends
on what the function does. A description of what the different lines means
follows here:

MODIFY [ registers ]
        This directive tells Watcom which registers are modified by our
        assembly function.  If the register is also in the parm list, it
        is not needed here.  If no registers are modified, then this 
        directive can be omitted.

EXAMPLE:
        If you have an assembler-function which puts a value into AX, the
        modify-line will look like this:

        modify [ AX ]


VALUE [ registers ]
        Since our assembly function looks like a C callable function,
        it should be able to return a value.  This directive tells Watcom
        which register we will be returning our value in.

EXAMPLE:
        If your assembly function returns a value in EBX, the value line would
        look like this:

        value [ EBX ]


PARM [ registers ]
        If your function uses parameters, you will need this line to tell
        Watcom which registers to put the parameters in.
EXAMPLE:
        Let's say you have a function-prototype like this:

        void any_function(long parm1, long parm2);

        If you want parm1 in eax, and parm2 in ebx, you simply write:

        parm [ eax ] [ ebx ]

        You are free to pass the parameters in any of the following 
        registers (or their 16 or 8-bit types), in any order: 

            EAX EBX ECX EDX ESI EDI

        You are not allowed to pass values in EBP.  There are ways to
        have watcom load the segment registers (ES, for example), but
        in 32-bit programming, this is seldom needed, and is beyond 
        the scope of this tutorial.


Hopefully, you have understood the basic stuff by now, so let's see what our
plot-function will look like:

void plot(long X, long Y, long colour);

#pragma aux plot =              /* eax == y, ebx == x, edx == color */ \
"mov edi,0xA0000"               /* protected mode screen-memory     */ \
"mov ecx,eax"                                                          \
"shl eax,8"                     /* y * 256                          */ \
"shl ecx,6"                     /* y * 64                           */ \
"add eax,ecx"                   /* y * 256 + y * 64 == y*320        */ \
"add eax,ebx"                   /* y * 320 + x                      */ \
"mov BYTE PTR [edi + eax], dl"                                         \
modify [edi ecx]                /* All registers used except for
                                   those in parm[]                  */ \
parm [ebx] [eax] [edx];

After studying this function, you will notice that each assembler-instruction 
is enclosed between two hyphenation-marks ("). You will also discover that 
each line must end with a slash (\), except for the last line which will end
with a semi-colon (;)
This is because Watcom expects all #pragma directives to be on one
line.  Adding a \ to the end of a line is C's way of continuing
the current line on the next; i.e. the compiler basically "ignores"
the newline after the \, treating the next line as if it was on this one. 

If you are using comments, as in this example, it is very important that
you use comments that are enclosed between /*  */, and not comments that 
start with //. If you use comments that start with //, the end-of-line slash
will be treated as a comment, resulting in compiler-errors.

Since this is a 32-bit program, we will almost always use the 32-bit 
registers like EAX, EBX, etc.  You are free to use the 8 and 16-bit 
registers; just remember that in some cases, speed hits can occur
when using these smaller registers.

Another important thing to remember is that in 32-bit protected mode, 
screen memory is no longer located at 0xA000, but at 0xA0000. Also, 
you will not need register pairs like ES:DI for the screen-memory 
anymore, since our segment registers are already loaded, and we can
now have 32-bit offsets. All in all, I think 32-bit programming is a 
lot easier.


Well, that's all there is to it, really. You can now call this function from
any program in the usual way.

One last thing... Watcom has a limit of 256 bytes *of assembled code*
for any one inline assembly block.  This can be somewhat restrictive,
if you plan on making long inline assembly functions.  It's hard to 
judge how long an inline assembly function can be, since each instruction
can be vastly different sizes.  On average, however, about 50 lines of
assembly is all you can have.


In a few weeks time you will wonder what all the fuss was about. It is so
easy once you know how... 

                        ******************************

My next tutorial will deal with using functions in external assembler files 
with Watcom. This is sometimes needed, because Watcom's in-line assembler is 
quite limited for larger functions with many parameters.

However, I don't know how to do this myself yet, so I will have to try
obtaining some information about it first. I just wish there was a tutorial
available that dealt with it...

                        ******************************

Would you like more programming info? If you haven't already checked out
my homepage, then you should do so now. It contains *LOTS* of useful
information for any programmer, though the info is aimed especially towards
game-programmers and demo-programmers alike.

The address is: http://www.oslonett.no/home/oruud/homepage.htm

                        *******************************

                                   DISCLAIMER

Even though it is very unlikely, I cannot be held responsible for any 
damage caused by using this tutorial.
