                 ------- Knut's Watcom tutorial #2 -------

                * Linking External ASM-files with Watcom *

GENERAL
-------

This is the second tutorial in my Watcom-tutorial series, and I will
continue where we left off in tutorial #1, by explaining how to use
external assembler files with Watcom. For this purpose I have chose to use
TASM instead of WASM because I personally think it is much better. If you
don't have TASM, then it shouldn't be difficult to make it compile with
WASM. What I will present in this article is a basic frame for an external
assembler-function, and we will write a small test-function which uses
many parameters. A basic knowledge of C++ and assembler is assumed.
All the files you need to follow this tutorial are included together with
this text-file.

I would like to thank the following people for helping me with this
tutorial:

John S. Price
Jay Stelly
Kirk M. Christiansen

This wouldn't be possible without your help!

A BASIC FRAME
-------------

A basic frame for an external asm-file would look something like this:

.386P
.MODEL FLAT


_TEXT SEGMENT DWORD PUBLIC 'CODE'
	ASSUME	cs:_TEXT, ds:_DATA

; ** Function-names can be inserted here **

; ** Variables can be inserted here **


; ** The function-definitions goes here **

_TEXT ENDS

   END

If you don't understand all of these directives then don't worry. You really 
don't need to change them. Anyway, what they basically do is to allow 
386-instructions and set the memory model to flat (so that there are no
segments).

OUR FIRST FUNCTION
------------------

OK, now that we know how to set up an assembler-file, we will write our
first function. This one will be very basic, without any parameters or
anything, just to check that things are OK.

What this function will do is to set the screen-mode to Mode 13h, using the
BIOS. A logic name for this kind of function would be something like
set_mode13h, so we declare the following in our assembler-file:

public set_mode13h

Note that we do not need to put an underscore in front of this function,
because TASM does this automatically.
To define our new function, we can use the following code:

set_mode13h     PROC    NEAR

mov     eax, 0013h
int     10h

ret

set_mode13h     ENDP

Easy! That's all there is to writing a simple procedure. To make things
more interesting, we will now write a function that uses parameters. This
isn't difficult either, once we know how. Our next function will use 5
parameters, and these will be printed to the screen, to show that the
parameters have been correctly transferred.
To be able to use parameters in TASM we will use a directive called ARG.
The syntax of this directive is like:

ARG     myparm1:size, myparm2:size....

Where size can be either BYTE, WORD or DWORD.
Listed below is our new function:

public  functest

functest        PROC    NEAR

push    ebp
mov     ebp,esp

ARG     PARM1:DWORD, PARM2:DWORD, PARM3:DWORD, PARM4:DWORD, PARM5:DWORD

mov     ah,02h
mov     edx,[PARM1]
int     21h
mov     edx,[PARM2]
int     21h
mov     edx,[PARM3]
int     21h
mov     edx,[PARM4]
int     21h
mov     edx,[PARM5]
int     21h

mov     esp, ebp
pop     ebp

ret

functest        ENDP

This function simply calls int 21h 5 times to print all our parameters on
the screen. The first two lines in the function makes ebp point to the
stack, so that it can be used to retrieve the parameters. The least two
lines restores EBP to its origial state. Now, I guess you
want to see our function in action, so we will write a small test-program
for the function, and we will also have to write a small header-file that
declares the function names for use with C++. Make a small file called
header.h and insert the following lines:

extern "C" {
void functest(long parm1, long parm2, long parm3, long parm4, long parm5);
}

Note! If you use plain C, the above will not work. Just remove the 'extern
"C"'-stuff and it will compile nicely.
Now we will write a short test-program for the function, testfunc.cpp. It
may look something like this:

#include "header.h"

void main()
{
    functest(1,2,3,4,5);
}

The only thing left now, is to compile and link the files. The easiest way
to do this is to put all the commands in a batch-file, like this:

@echo off
CALL wpp386 /d2 /3s testfunc.cpp
CALL tasm /mx /zi /os functest.asm functest.obj
CALL wlink system dos4g file testfunc,functest name testfunc

The first command, wpp386, is used to call only the C++ compiler. Remember
to use the /3s parameter, because this tells the compiler to use
stack-based calling conventions. When stack-based calling conventions are
turned on, all the parameters to a function is passed on the stack. The
/d2-switch is just for debugging, so you dont have to use it.

Next, we call TASM to compile our assembler-file. We also tell it to put
the resulting obj-file into functest.obj. Note that we cannot call this
file testfunc, because testfunc.cpp would be given the same name, and
thus be overwritten. The switches here are also for debugging, so you can
take away those if you want, too.

The final step is to link the files together and produce an executable
file. We tell the linker to name the file testfunc.exe by using the
directive 'name testfunc'.

Upon execution of the program you will see the first 5 characters of the
ASCII-chart printed to the screen. This wasn't especially hard was it? At
least I don't think so. I do not have any plans for writing more
Watcom-tutorials, but if you have an idea for a new tutorial then mail me,
and I'll se if I can do it. I hope you found this tutorial helpful and
informative, and I look forward to your comments and suggestions.

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

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.

