CHAPTER 18    Interfacing with Other Languages

 

Although Visual Prolog is an excellent tool for many purposes, there are still reasons to use other languages. For example, it's easier to perform numeric integration in C, and interrupt-handling and low-level stuff is perhaps better done in Assembly language. Moreover, if you've developed a large program in another language that already solves some aspect of the problem, this work should not be wasted. For these reasons, Visual Prolog allows you to interface your programs with other languages, as long as those languages produce standard object files and follows the conventions outlined in this chapter.

In this chapter you will find a number of examples of interfacing C and Visual Prolog. Their source files are in the DOC¡¬EXAMPLES directory or the FOREIGN directory of your distribution. In order to run them, you need to have the appropriate development system and/or C compiler and libraries installed.

The process to compile and link the examples varies considerably between the different operating systems and the different C compilers. In the foreign subdirectory of your distribution you will find thorough instructions and examples for the different platforms and compilers. Read these instructions carefully.

When using the Visual Prolog Development Environment, you don't, strictly speaking, need to know how to compile C programs, how to run the linker, or which libraries to specify and how to do it. This is handled automatically. However, you should have a fairly thorough understanding about C, and be able to write, compile and link multi-module C programs yourself.

Using DLL¡¯s

A dynamic-link library (DLL) is a binary file that acts as a shared library of predicates that can be used simultaneously by multiple applications.

Visual Prolog can generate dll¡¯s and link in dll¡¯s staticly or load dll¡¯s dynamicly. For more information about Visual Prolog and dll¡¯s please see the examples VPI¡¬EXAMPLES¡¬DLL and VPI¡¬TOOLEXAMP¡¬BUILD.

Calling Other Languages from Visual Prolog

In this section, we cover what you need to know to call C, Pascal and assembler routines from Visual Prolog.

Before calling routines and functions written in other languages, you need to declare them as external predicates in Visual Prolog. You also need to understand the correct calling conventions and parameter-pushing sequences, and you need to know how to name the different flow variants of your external predicates.

1. Declaring External Predicates

To inform the Visual Prolog system that a given global predicate is imple­mented in another language, you need to append a language specification to the global predicates declaration, as briefly mentioned in chapter 17:

    GLOBAL PREDICATES
        add(integer,integer,integer) - (i,i,o),(i,i,i) language c
        scanner(string,token) - (i,o) language pascal
        triple(integer,real)
  - (i,o) language asm

In Visual Prolog, you explicitly list the interfaced language; this simplifies the problems inherent in calling conventions, such as activation record format, naming convention and returning conventions.

2. Calling Conventions and Parameter Passing

The 80x86 processor family gives programmers a choice between NEAR and FAR subroutine calls, when running 16-bit programs. Visual Prolog requires all global routines to be FAR. The same applies to pointers to data objects. Many 16-bit compilers for the 80x86 family require you to choose between 16-bit and 32-bit pointers, where the 16-bit pointers refer to a default segment. In order to access all of memory, Visual Prolog always uses 32-bit pointers.

For 32-bit programs, "NEAR" means 32 bits and the above considerations are irrelevant.

(1) Input parameters

For input parameters, the value is pushed directly, and the size of the parameter depends on its type.

(2) Output parameters

An output parameter is pushed as a 32-bit pointer to where a values must be assigned.

(3) Return Values

Visual Prolog follows the most widely adopted register convention for function values on the 80x86 CPU family. This should not be of any concern in most cases, but is included here for completeness.

Table 18.1: Registers for Return Values

Operand Size

Program Type

16 bit

32 bit

byte (8 bits)

AL

word (16 bits)

AX

dword (32 bits)

DX:AX  ³  EAX

Pointers are 32 bits in size and are handled as dwords. The Program Type is determined by the operating system,

Floating point values are exceedingly troublesome to handle. They may be returned in registers, on the (emulated) coprocessor stack, and the pascal calling convention will frequently return them through pointers. Currently pascal functions cannot return floating point values. See the notes in the FOREIGN subdirectory of your distribution for any special considerations for your platform.

In any case, floating point values can always be returned in arguments. However, take special note that Visual Prolog's real corresponds to a C double (8 bytes).

You should also be aware that currently external C functions cannot return C structs (but they may of course return pointers to structs).

(4) Multiple declarations

In Visual Prolog, a predicate can have several type variants, arities, and flow variants, and a separate procedure is needed for each type and flow variant. When you implement predicates, having several versions, in C, each C function must have a name corresponding to the name generated by Visual Prolog. The naming convention used by Visual Prolog is straightforward; the predicate name is used as the root, and the suffix _X is appended to signify the variant number, where X is an integer starting at 0. If there is only one variant, no suffix is appended.

Consider the following program:

    GLOBAL PREDICATES
        add(integer,integer,integer) - (i,i,o),(i,o,i),(o,i,i),(i,i,i) language c
        square(integer,integer) - (i,o)

    GOAL
        add(2,3,X), write("2 + 3 = ",X), nl,
        add(2,Y,5), write("5 - 2 = ",Y), nl,
        add(Z,3,5), write("5 - 3 = ",Z), nl,
        add(2,3,5), write("2 + 3 is 5"), nl,
        square(5,Sq), write("5 squared is ",Sq).

A module linked with this program should contain the following C functions:

    add_0 for the first flow pattern (i,i,o)

    add_1 for the (i,o,i) flow pattern

    add_2 for (o,i,i)

    add_3 for (i,i,i)

    square

As an example, the following C module implements square as well as all flow patterns for add:

(5) Parameter pushing order

When interfacing to a routine written in C, the parameters are pushed onto the stack in reverse order and, after return, the stack pointer is auto­matically adjusted by Visual Prolog.

When calling languages other than C, the parameters are pushed in the normal order, and the called function is responsible for removing the parameters from the stack.

(6) Leading underscored

On the 16bit platforms, C compilers will prefix the name of public C functions with an underscore. Therefore, global predicates declared as language C will also have their name prefixed with an underscore if the target platform is one of these.

(7) NT naming convention

For the Win32 API, the number of bytes pushed on the stack will together with a dollar sign be suffixed to the predicate name. this means that a predicate p which has two integer arguments will be names p$8. When choosing the calling convention stdcall under Win32, this convention will be used.

(8) Converting the name to Uppercase (Pascal)

PASCAl uses the convention, that the name is converted to uppercase. So if language PASCAl is used, the name will during .OBJ module generation be converted to uppercase.

(9) Adjustment of stackpointer

There are two possibilities of adjusting the SP register . This can be done either by the called function or the calling function. Traditionally PASCAL does this in the called function, while C does it in the calling function.

Table 18.2: Calling conventions

 

Convert name to upper case

Add leading under Score

Push args Reversed

Adjust SP after return

NT naming convention

pascal

X

 

 

 

 

c

 

X

X

X

 

stdcall

 

X

X

 

X

syscall

 

 

X

X

 

(10) The AS "external_name" Declaration

As an alternative to the automatic naming convention, you can use the as keyword in the global declaration, like this:

    GLOBAL PREDICATES
        scanner(string,token) - (i,o) language c as "_myscan"

The result of this is that Visual Prolog will refer to the name _myscan in the object file instead of _scanner. You would still refer to the name scanner in your Visual Prolog source.

You can only use the as option if there is a single flow variant for the predicate.