In order for this site to work correctly, and for us to improve the site, we need to store a small file (called a cookie) on your computer.
By continuing to use this website, you agree to our cookies and privacy policy.
  
Home page Home page Home page Home page
Pixel
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel
Pixel

Reader's Forum - The C Interface Part 2 - Mark Hirst (Senior Techie - ICS)

Introduction

Part 1 dealt with the fundamentals of creating and compiling a C language module for inclusion within Rev. Part 2 will examine the methods used to move information between the R/Basic and C language environments. We begin by using a practical example of a C language routine that is used by R/Basic to return information about the DOS version of the host machine. The routine is used as a function within R/Basic to return the version as an integer.

Although this is a simple example, it shows how C code can be used to give R/Basic code access to fundamental operating system facilities. The DOS facility that is used in the example is part of a set of functions that are available to the application programmer for portable programming across the various IBM PC computers and their clones. In order to appreciate the relevance of these functions, we will briefly examine the r“le that the ROM BIOS and DOS functions play.

The ROM BIOS forms a layer between high level languages that form applications and the underlying hardware. The ROM BIOS services deal with fundamental tasks such as writing data on the screen or operating the disk drives. These services are standardised across the IBM PC family and are always incorporated in any enhanced version of a PC or its clone. This means that software will run across a wide range of PCs and clones and their new versions as they appear. The services fall into a number of groups including disk drives, video and keyboard. When a ROM BIOS service is used, the calling program does not have to be concerned about the location of the service in memory, simply that the service will perform the documented function. These basic services are enhanced by the DOS services provided by the familiar MSDOS/PCDOS operating system and provides a rich set of primarily disk orientated services. Once again, because these services are documented and always supported in subsequent versions of DOS, applications continue to run across versions of DOS. The services are all accessed by means of an interrupt, a process by which the processor stops processing, looks up an address in memory telling it where the service is found, runs it, and then resumes processing.

The intricacies of the ROM BIOS and DOS services and the use of interrupts are fortunately hidden in the high level languages that we generally use such as BASIC. If the language we use is rich, then many of the services provided at a low level are available in a high level, in an easy to use form. Since the high level services in our languages are in fact, collections of low level services in the ROM BIOS and DOS, our programs have portability. In the circumstances where our language does not provide the service we require, we must drop down a layer and communicate more directly with these low level services and it is for this reason that we might resort to a C language module and Revelation C interface to extend the functionality of R/Basic.

Calling an interrupt requires the programmer to store the number of the service he/she wants and any relevant parameters in the some of the registers of the CPU before invoking the service. After the service has been called and executed, some of these registers may contain useful results information which can be extracted. The 8086 chip contains fourteen 16 bit registers, four of which are known as the scratch-pad registers. Termed AX, BX, CX and DX, they are subdivided into two 8 bit registers, the high-order registers are called AH, BH, CH and DH, while the low-order registers are AL, BL, CL and DL. They are used as temporary storage areas and are used in arithmetic and counting operations. The example program demonstrates how some of these registers are used but it is beyond the scope of this article to explain the role of these registers in any further detail. The Programmers Guide To The IBM PC & PS/2 is recommended for further reading.

The C program uses the DOS interrupt 21h (decimal 33), function 30h (decimal 48) to return the DOS version. The major version number is returned in register AL, the minor version number is returned in register AH. The program multiplies the major version by 100 and adds that to the minor version number and then returns the value as an integer to the calling R/Basic routine.

      #define MSC 1
      #include <revintrf.h>

      main()
      {
       HANDLE hdl;
       ah_reg = 0x30;
       dosfunc();
       RevStoreInteger(al_reg*100+ah_reg, hdl.block);
       RevStoreReturnValue(hdl.block);
      }

The basic elements of the program should already clear from the previous example, namely, the definition of the compilation control variable MSC (indicating Microsoft C compiler), and the inclusion of the header file, revintrf.h used to define various global variables and routines incorporated in the C interface function library.

A number of elements are of immediate interest to the experienced C programmer, first, the way in which the registers are loaded and second, the execution of the interrupt itself and so the following paragraph is of particular interest to experienced C programmers.

The use of interrupts is done using a common block of variables defined in the #include file, revintrf.h. These common variables are loaded and examined by the programmer and replace the use of a REGS union. The invocation of the interrupt is performed by the dosfunc call which replaces the more conventional C call intdos. Another routine provided by the C interface, intfunc replaces the conventional C function int86. This routine would be required when using services outside of the general DOS services (interrupt 0x21) provided by dosfunc.

The program begins by declaring a special type of variable known as a structure. This is used later to return information back to R/Basic using functions provided by the interface. The program then loads a common variable, ah_reg to indicate that the DOS function we wish to use is 30h. The service is then invoked by the line, dosfunc(). The function returns data into the common variables al_reg and ah_reg and these contain the major and minor version numbers respectively; hence 3.30 will return 3 in al_reg and 30 in ah_reg.

The result is converted into a single integer and stored in the structure hdl using the C interface function, RevStoreInteger before making the value available to Rev using RevStoreReturnValue. The program is compiled, linked and converted into .BIN format and loaded into a Rev file (see REVMEDIA, October 1991). Calling is done using conventional R/Basic procedures. The following R/Basic code illustrates how this can be done.


0001        SUBROUTINE C_TEST
0002         DECLARE FUNCTION EXAMPLE2
0003         DOS = EXAMPLE2()
0004         CALL MSG('DOS Version is %1%','','',DOS/100)
0005        RETURN

The next part in the series will demonstrate the passing of parameters to the C language module and the calling of R/Basic routines from the C environment.

Bibliography

Readers interested in finding out more about the ROM BIOS and DOS services and the use of interrupts will find the following essential reading: The New Peter Norton Programmer's Guide To The IBM PC & PS/2, Peter Norton and Richard Wilton, Microsoft Press, ISBN 1-55615-131-4

Details on the screen and graphics services are explained in greater detail in the following book: Programmer's Guide To PC And PS/2 Video Systems, Richard Wilton, Microsoft Press, ISBN 1-55615-103-9

A number of texts are available on C programming, the most definitive of which is: The C Programming Language, Kernighan & Ritchie, Prentice Hall, ISBN 0-13-110362-8

(Volume 3, Issue 8, Pages 5-7)
Pixel
Pixel Footer R1 C1 Pixel
Pixel
Pixel
Pixel