Contents of the ABOUT.TXT file

Interfacing Assembly Language Routines with dBASE

by Ralph Davis

Creating Assembler Programs with DEBUG

DEBUG is the assembly language programmer's best friend. It is apowerful tool for exploring the computer's memory, testing assemblylanguage programs, studying program listings, and creating newprograms. Additionally, it can be used to rebuild corrupted datafiles, convert hidden files to accessible files, or simply analyzefile structures. Our main interest in DEBUG here is to createassembly language routines for use with dBASE II and dBASE III.

It is tempting to use DEBUG because of its interpreter-likequalities. You can quickly enter code and then see if it works.Ifit does, you call it .COM and write it to disk. If itdoesn't, you trace through the old code, enter new code, and tryagain. Eventually, you come up with a program that works throughtrial-and-error. However, this can lead to sloppy programminghabits and inefficient code, so it is important to bear in mindwhat you want a particular program to accomplish.

DEBUG has some limitations. Most importantly, it only recognizesabsolute addresses. When you write a program for submission toan assembler, you label the instructions and data you will needto refer to, then refer to them with the label. You don't needto know the actual addresses. DEBUG, on the other hand, obligesyou to look through your program listing and find addresseswhenever you refer to them. For instance, instead of enteringJMP EXIT, you must enter JMP 02FC. Instead of CALL HEXPRINT, youuse CALL 05AE. Instead of MOV BX, OFFSET DATA, you need MOV BX,0105. If your routine is small, this does not present a problem.But as you add features and it becomes larger, this becomes aserious impediment. If you add or alter instructions, therebychanging an absolute address, you have to change every referenceto it. And the only way to find the references is to pagethrough the entire program, line by line. For this reason, DEBUGis best for creating short utility programs.

Most often, programs created with DEBUG use BIOS or DOSinterrupts to manipulate the hardware. Some typical functionsthat appear in this issue are setting the cursor (see the exampleon page 4-72C of the Developer's Release Reference Manual and theprogram listed in this issue), manipulating the shift keys, orswapping printer ports. Programs of this type should not containany subroutines.

DEBUG has another important limitation: it only understandshexadecimal numbers. There is simply nothing you can do to makeit accept decimal numbers. This is not a problem when enteringaddresses or interrupt numbers, as most assembly languageprogrammers think these values in hexadecimal anyway. But veryfew programmers think in hex when doing calculations. DEBUG istherefore not a good tool for doing number-crunching of evenintermediate complexity. Although there are utilities availableto assist in this process, such as Sidekick, this is still amajor obstacle to doing extensive calculations within DEBUG.

Another problem with DEBUG is that code produced with it can beextremely obscure. Trying to decipher the flow of a programwhere you have only absolute addresses and hexadecimal numbers toguide you can be very frustrating. In addition, DEBUG does notsupport comments. So when you read a DEBUG listing, you are, forall intents and purposes, reading "machine English." The machineexpresses its own language in cryptic English-like symbols,making a few grudging concessions to your desire to understandit. All of this reinforces what we suggested earlier: keepDEBUG routines short.

The program from the Developer's Release Reference Manualmentioned above is a good example of a program appropriate forDEBUG. The listing on page 4-72C is as follows:

This is a terse routine that converts the dBASE III cursor to afull-sized box when CHR(18) passed as a parameter to it. Noticeone thing about this code: it has six lines of assemblerdirectives (the first three and the last three), and only fourlines of machine instructions. In a short program like this one,there is no advantage to assembling, linking, and converting itusing MASM, LINK, and EXE2BIN. DEBUG is faster and easier.

Here is a DEBUG session that enters this program as a .COM file.

(The DEBUG commands are explained in Chapter 8 of the PC/MS-DOSmanual. Page numbers which follow refer to it.)

Notice that 'INT 20' is our last instruction, not 'RET' as themanual indicates. We will explain this shortly.

The address following the last instruction is 108. Therefore,enter eight into CX using the 'R' (register) command [page 8-41].This tells DEBUG the number of bytes to write to disk.

-RCX CX 0000 :8

Name the program CURSOR.COM using the 'N' command [page 8-37],and write it to disk using 'W' [page 8-55].

-NCURSOR.COM -W Writing 0008 bytes

This is the basic procedure for creating a .COM file from DEBUG.CURSOR.COM will yield unpredictable results executed fromPC/MS-DOS, since the registers are not preserved, and we have noway of knowing what is being passed in DS:BX. (When we testedit, the cursor simply vanished.) Nor, in its present form, willit work in dBASE III. It needs a couple of changes to make itwork, but this point deserves some attention.

PC/MS-DOS .COM files and dBASE LOAD modules require slightlydifferent specifications. A .COM file must be ORGed (originated)at address 100H, and it must end with a command like INT 20H(terminate) or INT 27H (terminate and stay resident); a simpleRET will not return correctly. dBASE III, on the other hand,requires LOAD modules to be ORGed at address 0 and to return todBASE III with a far return, RETF. If you load a conventional.COM file, ORGed at 100H and terminated with INT 20H, into dBASEIII, and then call it, you will lock the system, even if it worksfrom PC/MS-DOS. When DEBUG writes a program to disk, it writes abinary file -- that is, a file which contains nothing but themachine instructions you have given it. Therefore, we need notconcern ourselves with ORGing programs correctly at this stage.We do have to terminate LOAD modules with RETF, however.

Here is a DEBUG session that enters this program as a .BIN filewhich will execute from dBASE III.

The page of the Developer's Release Manual referred to abovegives the following example of how to use Cursor:

LOAD Cursor STORE CHR(18) TO shape CALL Cursor WITH shape

The commands to convert the cursor back to its normal format are:

LOAD Cursor STORE CHR(12) + CHR(11) to shape CALL Cursor WITH shape

.COM Files vs. .EXE Files

When creating programs with a full-featured assembler, we havetwo options: .COM files and .EXE files. Each has advantages anddisadvantages.

.COM files are an inheritance from the world of 8-bit CP/M. Theyare your only option if you have a CP/M machine. .COM files mustadhere to a strictly defined structure.

1. They must fit entirely within one segment. All segment registers must point to the same address, and cannot be changed during the execution of the program. This means that all of our main program, subroutines, and data must fit in 64K. A 64K .COM file is a very large program -- each line of code assembles to between 1 and 6 bytes, so a 64K .COM file could have as many as 30,000 lines of source code.

2. They must be ORGed at 100H. When PC/MS-DOS loads a .COM file, it jumps to CS:100H and begins executing.

3. They must return control to their calling routine with either INT 20H or INT 27H, or the equivalent INT 21H function calls, 4CH and 31H.

.COM files load more quickly than .EXE files, since no addressesneed to be calculated at load time.

The assembly language programs that dBASE II and dBASE III canexecute as subroutines (with the CALL command) are variations ofthe .COM file. We will discuss the specifics of their formatslater.

.EXE files are less limited structurally. The segment registerscan be freely manipulated, and each one can point to an entirelydifferent 64K segment. .EXE files can therefore be much largerthan .COM files. .EXE files were designed to take betteradvantage of the actual architecture of 16-bit 8086-basedmicroprocessors. Having data in one segment, code in another,and the stack in a third allows much greater utilization of thememory space available in today's machines. It also provides usthe semblance of structured programming in assembly language.The SEGMENT, PROC, ENDS, and ENDP operators give a programlisting a much more organized appearance than it has with JMP andDB statements interspersed throughout the code.

.EXE files take longer to load than .COM files, as many of theabsolute addresses are not computed until load time. They alsotake up more disk space than .COM files. However, since they usemuch more of the 8086 family's capabilities, they can be muchmore powerful programs. The commercial programs which werehanded down from the CP/M world are all .COM files, whereas thosewhich were created since the advent of 16-bit machines are mostly.EXE files.

Having said this, we will leave .EXE files behind. You cannotLOAD .EXE files from dBASE II or dBASE III. You can execute themwith QUIT TO in dBASE II or RUN(!) in dBASE III. If you want topass parameters to and from .EXE files, you must pass them intext files (the SDF format is recommended).

Adapting Assembly Language Programs to dBASE II or III

As mentioned earlier, the format of a dBASE II or III assemblylanguage subroutine most closely resembles that of a .COM file.Most importantly, it must reside in one segment. Since it isintended as a subroutine, not as a stand-alone program, it willdiffer somewhat from a standard .COM file.

For one thing, a .COM file must be ORGed at 100H. However,ORGing a dBASE (II or III) subroutine at 100H will cause it tofail. A program intended for use in dBASE II must be ORGed highin the code segment -- the exact address depends on the versionof dBASE II, the later the version, the higher the address. Inversion 2.43*, the ORG address should be above 61440 decimal.(See Robert Boies' article on swapping printer ports in theAugust issue of TechNotes for a good example of a dBASE IIassembly language program.) A program intended for dBASE IIImust be ORGed at 0 (that is, it need not have an ORG statement).

The procedure for converting assembly language source code intoprograms dBASE II or III can execute are as follows:

1. For dBASE II, you must assemble your program with an assembler that produces a file in Intel .HEX format. Intel's assemblers, ASM (for CP/M) and ASM86 (for CP/M-86), create such a file. For PC/MS-DOS, the Seattle Computer Products assembler generates a .HEX file. Refer to their manuals, as their assembly language syntax differs somewhat from Microsoft's and IBM's.

2. For dBASE III, use the IBM or Microsoft Macro-Assembler (MASM.EXE) to produce a .OBJ (object) file. Enter the command as follows:

MASM ;

The third parameter will cause MASM to produce a listing file with a .LST extension, which is very useful for debugging.

3. Use the linker utility (LINK.EXE) that comes both with PC/MS-DOS and with the assembler. This will create an .EXE file. The command is:

LINK

Press Return three times in response to the prompts.

4. Use EXE2BIN.EXE to convert the program to .COM or .BIN format. If you are creating a .BIN file, you need only enter one parameter in the command line:

EXE2BIN

If you are creating a .COM file, you need to specify the full target filename:

EXE2BIN .COM

Using Conditional Assembler Directives

Because the differences between .COM files and .BIN files areminor, it is possible to generate both using the same sourcecode. The following program skeleton shows how to set this up.The EQU statements at the top inform the assembler whether we areassembling a program for PC/MS-DOS or dBASE III. In the presentexample, we have set COM equal to 0 (meaning false) and D3 equalto 1 (non-zero, meaning true). We then use conditionaldirectives to tell the assembler how we want the program created.Conditional directives are statements in your assembly program todirect the assembler to assemble a block of instructions based ona variable value. For example, IF COM (if COM is not zero), ORGthe program at offset 100H. Then at the end of the program, IFCOM, exit with INT 20H; otherwise, exit with a far RET.

It is very important to load the DS register with the segmentaddress contained in CS. PC/MS-DOS does this automatically for a.COM file, but dBASE III does not. Therefore, if your routineneeds to access its own data, it will need to set DS correctly.

Sample Program With Conditional Assembly

Here is an program built on the skeletal structure which sets condensed print on an EPSON printer.