Hints for writing simple programs for both OS/2 and DOS

        One of the most obvious characteristics of OS/2 is its similarity to
    DOS.  Many of the commands are the same, and if you are running in a full
    screen session it can sometimes be a while before you notice whether you
    are using DOS or OS/2.  In fact, to solve this problem my PC is set up to
    have "DOS" or "OS/2" embedded in the command line prompt!

        A major advantage of this similarity is that if you are trying to use
    both OS/2 and DOS the familiarisation time is reduced.  However not many
    people seem to be aware of the various methods available to do this for
    their own programs.  This article attempts to briefly outline one or two
    or the ways in which a program can be made to run in a similar manner under
    both DOS and OS/2.


        The most "primitive" way to have similar commands under DOS and OS/2
    is to have two separate binary programs and then set the PATH environment
    variable so it points to the OS/2 program for OS/2 and vice versa.  This
    technique is just like having an 'ls' program under DOS which does almost
    the same as the UNIX command of the same name.


        However, when the two operating systems are related as closely as DOS
    and OS/2 another technique is very powerful - the concept of a 'bound'
    program.  This is an executable program which runs under BOTH operating
    systems.  It is possible to do this because the header for the OS/2 loader
    always follows a header for the DOS loader.  Usually this DOS header merely
    loads a 'stub' program which prints the message: "This program cannot be
    run in DOS mode.", but the program 'BIND' will replace this stub with a DOS
    program which will load the OS/2 program and map the OS/2 specific calls
    into calls to code providing equivalent function by, for example, using DOS
    interrupts.

        Example:- the standard learning program 'hello.c' to print out
        'hello world' can be compiled for OS/2 and then 'bound' to run
        under DOS as well by:

                cl /c hello.c
                link hello ;
                bind hello c:\lib\doscalls.lib

        and now the HELLO.EXE output program will run under OS/2 or DOS

        (I am using MicroSoft C5.1, installed for OS/2 programming)

        Note that BIND, being a simple minded program, has to be explicitly
        told where to look for the file DOSCALLS.LIB.  The CL command can do
        this part for you - the three lines above can be replaced with:

                cl /Fb hello.c

        which is a lot simpler, but hides the mechanics of the operation.


        BIND uses another library in addition to DOSCALLS.LIB: API.LIB,
    which contains the code for DOS to provide some of the OS/2 application
    programming interface (abbreviated as API, hence its name).
    The APIs supported by this library are known as Family APIs (FAPI for short)
    and this means that any program restricting itself to the FAPI calls to OS/2
    can be bound in this manner.  This includes (almost) all programs which only
    use the standard C runtime fuctions.

        The FAPI includes functions like DosWrite(), VioWrtTTY(), KbdCharIn(),
    but not OS/2 specific items like DosCreateThread().  Note also that some of
    the calls are only partly supported under DOS - for example DosFindNext()
    is restricted under DOS to only one search handle at a time.


    BINDing a program is an extremely attractive idea for two main reasons:
      - Firstly you have ONE source file and ONE executable file.  Testing,
        maintaining and keeping track of the program is easier.
      - Secondly you can develop and test your program under OS/2, which has a
        better designed API than DOS and a more robust debugging environment,
        and then be confident of the results under DOS.

    What are the problems?
      - Firstly the bound program can only use the features which are
        common to both OS/2 and DOS, so the OS/2 program is restricted by DOS.
      - Secondly the program will be slower under DOS than if it had originally
        been compiled solely for DOS because of the extra loading stage.
      - Thirdly some of the FAPI calls are NOT quite identical under OS/2 and
        DOS, and some have side effects with, for example, the C runtime.


    There are however various ways round some of these problems.

        The first way is to return to two different executables - but to link
    them together into one binary, by using the DOS program as the 'stub' for
    the OS/2 program.

        Example:-

            masm prog.asm;
            link prog,dosstub.exe;
            echo STUB 'dosstub.exe' > prog.def
            cl /DOS2 prog.c prog.def

        In this example I've assumed you first had a DOS assembler program and
    since then have written an OS/2 equivalent in C.  This is merely a way of
    combining two programs into one - the DOS and OS/2 programs can be quite
    different, and so the OS/2 program can have more 'bells and whistles'.
    It is also a useful technique where the ASM program is very quick and the
    bound C one isn't!


        The second way is to use DosGetMachineMode() to find out whether you are
    running DOS or OS/2 and act accordingly.  For example, a program may wish to
    'pop up' an error window.  Under OS/2 VioPopUp may be used, but this is NOT
    a FAPI call.  So the program could conditionally call VioPopUp only in OS/2.

        BIND allows you to specify, by the option "-n @filename", a file which
    contains a list of APIs to map to a BadDynLink function and so prevent
    unresolved references at bind time, which in this example would contain
    VIOPOPUP and VIOENDPOPUP.


        You can also write your own code to provide a suitable equivalent to an
    OS/2 API and link that with BIND. I have used this for programs which use
    using NETBIOS, which under DOS is accessed by interrupt 5Ch but under OS/2
    is a call.

        Example:- a short assembler program NETBIOS.ASM is written:

            code    segment byte public 'CODE'
                    assume  cs:code

                    public  NETBIOS
            NETBIOS proc    far
                    push    es                      ; save registers
                    push    bx
                    les     bx,dword ptr [bp+4]     ; address of control block
                    int     05ch                    ; do the work!
                    pop     bx                      ; restore registers
                    pop     es
                    ret
            NETBIOS endp

            code    ends
                    end

        I then compile my program (called, for example, netb.c) like this:

            masm netbios.asm;
            cl netb.c
            bind netb netbios.obj c:\lib\doscalls.lib


        BIND will locate the NETBIOS procedure in the specified object module
    and use this code to replace the OS/2 NETBIOS API.  In this way you can
    provide your own DOS functionality for OS/2 DLLs which do not belong to the
    FAPI but which are achievable under DOS.


      In conclusion, there are 20 million machines or so in the world which
   are running DOS, so it is likely that even the keenest advocate of OS/2
   will need to use DOS, or at least to help others to use it.  It is worth a
   little effort to try and make this task as easy as possible and to reduce
   the learning curve for those upgrading from DOS to OS/2.  Methods such as
   these are a useful part of this process.


                                                     Roger Orr    OR/2 Limited