Multiple Object Module Programs


ASIC programs can incorporate subroutines you write in ASIC or assembly language. This is a powerful feature, but it is a bit complicated until you have done it a few times. This chapter describes how to use the CALL SUB statement to call separately compiled subroutines.


If you are developing an ASIC subroutine, all you need is ASIC, and a linker such as MS DOS LINK or Borland's TLINK. Creating an ASIC subroutine is described in Chapter 7 ("SUB/ENDSUB" statements). This chapter will provide additional information for compiling the subroutine and incorporating it in your ASIC programs. ASIC subroutines are much easier to develop than assembler subroutines.


For Assembly Language subroutines, you need: ASIC, and a linker such as MS DOS LINK or Borland's TLINK. You will also need an assembler such as Borland's Turbo Assembler or Microsoft's Macro Assembler. Later in this chapter we'll describe the details of building an Assembly language subroutine.


The main difference between developing subroutines in ASIC or Assembly Language is the coding of the subroutine itself. Once the subroutine has been written, the process of building the ASIC program is very similar.

In both cases, the "CALL SUB" statement is used to call the subroutine--it doesn't matter if you are calling ASIC subroutines or Assembler subroutines, the syntax of the CALL SUB statement is the same for both.

You can also store multiple .OBJ files into one or more .LIB files (if you have a program such as Microsoft's LIB or Borland's TLIB) and use the "Library Names" compile option. (see Chapter 5)

To help you get started, ASIC includes two sets of sample files that contain everything you need to create a working sample ASIC and Assembler subroutine.

ASIC Sample Files

CALLSUB.ASI <-- Program to call ASIC subroutine
CALLSUB.PRJ <-- Project file for CALLSUB.ASI
TESTSUB.ASI <-- ASIC subroutine called by CALLSUB.ASI
TESTSUB.PRJ <-- Project file for TESTSUB.ASI
Assembler Sample Files
CALLSRCH.ASI <-- Program to call Assembler subroutine
CALLSRCH.PRJ <-- Project file for CALLSRCH.ASI
SRCH.ASM     <-- Assembler subroutine called by CALLSRCH.ASI

We'll go through the process of building the ASIC subroutine version first. Then, we'll build the assembly language version. Finally, we'll describe requirements for assembler language subroutines.


Make sure the sample files (TESTSUB.PRJ, TESTSUB.ASI, CALLSUB.PRJ, CALLSUB.ASI) are in your ASIC working directory for purposes of this example. Also, copy the LINK.EXE file which comes with MS DOS to your ASIC directory or to the directory "C:\DOS". Note: if you don't want to make a copy of LINK.EXE, you don't have to, but you need to tell ASIC how to find LINK.EXE by using the LNK= compile option (see Chapter 5 command line options, or the ADVANCED MENU OPTIONS section in chapter 3, for information on setting the LINK path).

The first step is to compile all of the subroutine files. Their are two compiler options which ASIC will force you to use with subroutine files. The "Object Output Files" option must be set. The "Debugging Code" option must be turned off. The TESTSUB.PRJ file already has these set properly, but you need to know about them for future projects. To build the object file from the integrated environment type:


You shouldn't get any compiler errors, so press <enter> to clear the "No Errors" message box. Then, you can exit ASIC.


If you prefer to build the subroutine from the command line compiler, type:


Regardless of which of the two approaches you followed, the result is that ASIC has compiled your subroutine into the file "TESTSUB.OBJ". You are now ready to compile the calling program.

To build it from the command line, type:

               ASICC CALLSUB.PRJ <enter>
               ASICC CALLSUB B/OBJ OBJ=TESTSUB <enter>

To build it from the integrated environment, type:

               ASIC CALLSUB <enter>

You shouldn't get any compiler errors, so press <enter> to clear the "No Errors" message box. Then, you can exit ASIC. If you did get an error, it was probably because ASIC could find LINK.EXE (see above). The following statement will exit ASIC:


The CALLSUB.PRJ file took care of a couple details for you automatically. First, it set the "Object Output File" compiler option. When using CALL SUB this option must always be set. Second, it identified the name of the ".OBJ" file containing the subroutine we are calling, in this case "TESTSUB.OBJ". This option is set with the "Object Names" option on the advanced compiler menu. CALLSUB and TESTSUB are described in Chapter 7 under the SUB/ENDSUB statements. So if you want more information about them, check there. The program can be run now if you want. To do so, just type "CALLSUB <enter>".


The file CALLSRCH.ASI is an ASIC program which calls a subroutine called SRCH. SRCH.ASM is the source to an assembly language program. Make sure these files (and CALLSRCH.PRJ) are in your ASIC working directory for purposes of this example. Also, copy the LINK.EXE file which comes with MS DOS to your ASIC directory or to the directory "C:\DOS". Note: if you don't want to make a copy of LINK.EXE, you don't have to, but you need to tell ASIC how to find LINK.EXE by using the LNK= compile option (see Chapter 5 command line options, or the ADVANCED MENU OPTIONS section in chapter 3, for information on setting the LINK path).

The first step in producing the EXE file is to assemble the SRCH.ASM program. If you have Borland's Turbo Assembler, type:

     TASM SRCH <enter>
     Otherwise, if you have Microsoft's Macro Assembler, type:
     MASM SRCH <enter>

This should create a file called SRCH.OBJ in your working directory. The second step is to build the ASIC program. If you prefer the ASIC command line compiler, you can accomplish the build with a single command:


That's all there is to it, if you are building from the command line. You should be able to run the program now, try it--type:

     CALLSRCH <enter>

Read the comments in the programs for a description on what the program does. You can also accomplish this easily from the integrated environment.

     First, load the program into ASIC:

     CALLSRCH <enter>

The above opens the "file" menu, selects the "open" option, and selects the file "CALLSRCH.ASI". Next, set the compiler options:

     SRCH <enter>

This opens the "compile" menu, selects the "advanced options" menu, sets the file output type to "obj" and set the "obJect names" to "SRCH".


This opens the "file" menu, and selects the "save" option. This not only saves the CALLSRCH.ASI file to disk, it also saves all of the compile options  you just set, to a file called CALLSRCH.PRJ. So, the next time you enter ASIC, you won't have to re-set all those options for this program.

Now compile the program:


You should see the ASIC compiler screen compile the program, and you should then see a few messages as the Microsoft LINK program builds the EXE file. In the future, from the integrated environment, you can rebuild the CALLSRCH program by loading it into the edit buffer and pressing <F10> to compile. ASIC will remember all of the options you told it, so it will remember to link the "SRCH" module, automatically.


Now that you know how to build the program we'll step back and explain how
to develop and use assembler subroutines. Four steps are required:


The first step is to develop an assembler subroutine. There are
several things you need to be aware of, when developing an assembler
  1. The entry point must be declared using the PUBLIC keyword. If you do not do this, you will receive a linker error. 
  2. ASIC saves most critical registers prior to the CALL, however, in your assembler subroutine, you must take care to preserve the value of the SS and SP registers. When you return to ASIC from your subroutine, you must insure that the SS and SP register values have not changed. This means that if you PUSH any values to the stack, you MUST POP them before the return instruction to ASIC. Failure to do so will almost always crash your program. 
  3. ASIC calls external assembler subroutines using FAR calls (segment:offset), so you must declare the PROC as FAR, and must use the RETF (far return) instruction to return to ASIC. 
  4. ASIC passes parameters on the stack as far pointers (by reference). This means, that regardless of whether the parameter being passed is a short integer, long integer, or string, ASIC will always place 4 bytes on the stack. ASIC always pushes the segment portion (2 bytes) of the pointer to the stack first, followed by the offset (2 bytes). Remember that these 4 bytes are not the value of the variable, but a pointer to the variable in memory. If you look at the memory location pointed to by these 4 bytes, you will find the starting location of the variable. ASIC passes variables from the CALL SUB statement from left to right. 
  5. When your subroutine receives control from ASIC, the stack will look like the following:
    SS:SP+10  (2 bytes) segment of next-to-last parameter
    SS:SP+8   (2 bytes} offset of next-to-last parameter
    SS:SP+6   (2 bytes) segment of last parameter
    SS:SP+4   (2 bytes) offset of last parameter
    SS:SP          (4 bytes) return address

  6. If your ASIC calling program passes constants to a subroutine, be aware that the subroutine can update the memory location used to store the constant values. For example, if you modify the memory location used by ASIC to store the value of the constant "1" to "2", you will have inadvertently redefined the constant "1" to "2" throughout your ASIC program. If you are not sure if a parameter will be updated by a subroutine, play it safe, use a variable for the parameter.


Before you subroutine can be used, it must be assembled. Use your favorite assembler to do so. If you like, you can store your object files in Microsoft LIB compatible format files for convenience.


All that you have to do to call the subroutine, is insert a CALL SUB statement in your program. The CALL SUB statement has the following format:

          CALL SUB "subroutinename" [parm1...parmN]

Where "subroutinename" is the name of the subroutine you wish to call (it must be in quotes). No parameters are required, but you may provide one or more, if you like. Any valid ASIC variable or constant type may be passed as a parameter. "subroutinename" must match the name of the PUBLIC PROCedure you declared in your assembly language subroutine.


Finally, you have to build the program. You must do the following steps to create a valid EXE file:
  • Load the edit buffer with the calling ASIC program 
  • Select the "Obj output file" option. 
  • Identify the names of any object modules to be linked with your ASIC program, if any. 
  • Identify the names of any object libraries to be linked with your ASIC program, if any. 
  • Tell ASIC where to find the MS DOS Linker 
  • Compile your program--ASIC will call the DOS linker automatically.


The above is a simple example; there are other compile examples in Chapter 5. If you have one or more subroutine object modules stored in a library, you can use the "LIB=" option on the command line to specify the library names to ASIC. You can also specify the library information inside the integrated environment (Open the "compile" menu, select the "Advanced options" option. From the "Advanced options" menu that pops up, select the "liBrary names" option.

When specifying object files, remember that the ".OBJ" file extension is optional, as is the ".LIB" extension on library files. You don't have to give the full file spec (i.e., disk drive and directory) if the file is in the ASIC directory. See Chapter 3 and Chapter 5 for more information on setting these options from the integrated environment and command line compiler respectively.


You can use the debugger on ASIC programs which use the CALL SUB statement, however, you can't "trace into" the subroutine instructions. When you press <F8> to trace one line, and you are positioned on a CALL SUB statement, the CALL SUB is executed, and ASIC advances to the next ASIC statement following the CALL SUB. The called subroutine should not contain debugging (i.e., "INT 3" in assembler) instructions. If they do, the debugger will behave erratically (this can never happen with ASIC subroutines--only with assembler subroutines).