Look
Toward
the
Past
Published in Greg Lief's Aquarium, Nov. 1995 Issue
If I had to describe the secret to good Clipper programming in a single phrase I would have to say "look toward the past with an eye toward the future."
Why would I suggest looking backwards? Because programming in Clipper is not significantly different from programming in C, Pascal or BASIC nor would I imagine particularly different from programming in Ada, COBOL, Forth, FORTRAN or SmallTalk.
Oh sure the syntax is different and the structure is different and memory management is different and about a million other things are different but other than that, they're identical.
And to me anyway, that's great! Why, because it means that the resource pool of information relevant to the Clipper developer is far larger than we are ordinarily led to believe.
Information is all around us...
To tell you the truth, it's a rare moment when I pick up a Clipper book for information on programming. Sure I check the manual when I forget the order of function arguments or pop up a Norton Guide file to check the return value of DBSKIP() (it returns NIL by the way though for the life of me I can't see why) but the sections on recursion, linked-lists, binary trees and such is... well "non-existent" pretty well describes it.
And no, I'm not suggesting that you should forget the obvious sources of Clipper programming ideas but rather that you should not overlook the less than obvious sources.
Personally I find books on C and C++ extremely useful as well as non-language specific books on the fundamentals of application design and over the years I've read books on OOPs, Actor, Turbo Assembler, MSVC++, Windows, X-Windows, Ada, writing compilers and the evolution of computer languages.
To list just a few:
Object-oriented Software Construction by Bertrand Meyer 1988 Prentice-Hall International (ISBN 0-13-629049-3) Object-Oriented Programming Featuring Actor by Marty Franz 1990 Scott, Foresman and Company (ISBN 0-673-38641-4) Compiler Design in C by Allen I. Holub 1990 Prentice-Hall, Inc. (ISBN 0-13-155045-4) Reusability & Software Construction by Jerry D. Smith John Wiley + Sons (ISBN 0-471-52411-5) Programming, The Impossible Challenge by Bob Walraet North-Holland (ISBN 0-444-87128-4) Object-Oriented Software by Ann L. Winblad Addison-Wesley (ISBN 0-201-50736-6) Managing the System Life Cycle by Edward Yourdon Yourdon Press (ISBN 0-917072-26-X) Analysis, Design & Implementation of Data Dictionaries by Ken S. Brathwaite 1988 McGraw-Hill (ISBN 0-07-007248-5) The Practitioner's Blueprint for Logical and Physical Database Design by Eric Garrigue Vesely 1986 Prentice-Hall (ISBN 0-13-694267-9)
Two standard references, never far from my desk, are The C Programming Language also known as the "white book", because of its white cover and The Elements of Programming Style.
The C Programming Language by Brian W. Kernighan and Dennis Ritchie 1978, 1988 Prentice-Hall, Inc. (ISBN 0-13-1103262-8) The Elements of Programming Style by Kernighan and Plauger 1974 McGraw-Hill Book Company (ISBN 07-034199-0)
But, to get to the point. I'm simply saying that the equation we use to center a message on the screen, that leap year algorithm, that 24 to 12 hour time conversion function, that random number generator and that shuffle routine along with so many other things that we labor over were already written long ago.
Nothing new under the sun...
Pythagoras didn't program in Clipper and neither does Brian Kernighan or Niklaus Wirth (as far as I know) but their accomplishments are no less useful for it. Luckily they did use C and Pascal (except for Pythagoras) and armed with the source code (or the formula) we can usually translate to Clipper fairly easily.
Before you scream "but I don't know Pascal" let me point out that you don't actually need to know Pascal or C to figure out what's going on in a short routine. Though I admit familiarity would help, one can often pick up the algorithmic "signal" while filtering the language-specific "noise".
Good information is good information regardless of the language used. Read for instance just a few of the many one-liners from The Elements of Programming Style, a book first published in 1974.
Though written 20 years ago these examples apply to our Clipper work today and the admonition against comparing floating point values is of particular interest. Hardly a month goes by where someone doesn't rediscover this fact and reports "floating point bugs" on the Clipper Forum (CA's forum on CompuServe) or on the Internet newsgroup, comp.lang.clipper.
A simple example using C...
In the book, The C Programming Language a very simple example is used to illustrate C syntax and program structure. The code, reprinted here, outputs a table of Fahrenheit temperatures with their centigrade equivalents.
/* fahr.c */
#include <stdio.h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */
main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0; /* lower limit of temperature table */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr - 32) / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
}
I don't know what C code looks like from the "never having seen C code perspective" but I hope it looks familiar enough so that you recognize the following Clipper translation.
/* fahr.prg */
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */
FUNCTION main()
local fahr, celsius
local lower, upper, step
lower = 0 /* lower limit of temperature table */
upper = 300 /* upper limit */
step = 20 /* step size */
fahr = lower
while (fahr <= upper)
celsius = 5 * (fahr - 32) / 9
qout(fahr, celsius)
fahr = fahr + step
end
return 0
I purposely kept the code close to C but this was not my main point, rather I wanted to illustrate that with minor adjustments (in this case a few semicolons, some braces and a handful of variable declarations), the resources of the last 30 years are available today.
An interesting example using Pascal...
Lest you think that I'm suggesting you spend the next few months pouring over C books, I present the following example from Mastering Turbo Pascal.
Mastering Turbo Pascal 4.0, Second Edition by Tom Swan 1988 Hayden Books (ISBN 0-672-48421-8)
The following code performs a binary sort and is given as an example of how to write in Turbo Pascal.
{ sortdemo.pas }
PROGRAM BinarySort;
CONST
MaxElements = 100; { Maximum array size }
TYPE
Element = Integer;
ElementArray = ARRAY[ 1 .. MaxElements ] of Element;
VAR
a : ElementArray;
i, n : Integer;
PROCEDURE Sort( VAR a : ElementArray; n : Integer );
{ n = actual number elements in array a }
{ Algorithm = Binary Insertion }
VAR
i, j, Bottom, Top, Middle : Integer;
Temp : Element;
BEGIN
FOR i := 2 to n DO
BEGIN
Temp := a[ i ]; Bottom := 1; Top := i - 1;
WHILE Bottom <= Top DO
BEGIN
Middle := ( Bottom + Top ) DIV 2;
IF Temp < a[ Middle ]
THEN Top := Middle - 1
ELSE Bottom := Middle + 1
END; { while }
FOR j := i - 1 DOWNTO Bottom DO
a[ j + 1 ] := a[ j ]
a[ Bottom ] := Temp
END { for }
END; { Sort }
BEGIN
Writeln( 'Binary Insertion Sort' );
Writeln;
REPEAT
Write( 'How many ? (2 to ', MaxElements, ') ? ' );
Readln( n )
UNTIL n <= MaxElements;
FOR i := 1 to n DO
BEGIN
a[ i ] := Random( Maxint );
Write( a[ i ]:8 )
END; { for }
Writeln; Writeln;
Sort( a, n );
FOR i := 1 to n DO
Write( a[ i ]:8 );
Writeln;
END.I think Pascal looks "English" enough that a translation to Clipper shouldn't require reading more than a few pages from a Pascal manual if we get stuck along the way. We can guess that CONST means "constants" and that VAR means "variables", writeln() is an output function, etc.
// sortdemo.prg
// PROGRAM BinarySort
// CONST
#define MaxElements 100 // Maximum array size
// TYPE
memvar getlist
// VAR
STATIC a
STATIC i, n := 0
PROCEDURE Begin
a := ARRAY( MaxElements )
Main()
PROCEDURE Sort( a, n );
// n = actual number elements in array a
// Algorithm = Binary Insertion
// VAR
LOCAL i, j, Bottom, Top, Middle
LOCAL Temp
// BEGIN
FOR i := 2 to n
Temp := a[ i ]; Bottom := 1; Top := i - 1
WHILE Bottom <= Top
Middle := Int(( Bottom + Top ) / 2)
IF Temp < a[ Middle ]
Top := Middle - 1
ELSE
Bottom := Middle + 1
ENDIF
END
FOR j := i - 1 TO Bottom STEP -1
a[ j + 1 ] := a[ j ]
NEXT j
a[ Bottom ] := Temp
NEXT i
// END { Sort }
PROCEDURE Main
QOut( 'Binary Insertion Sort' )
QOut()
WHILE (.T.)
@ Row(), Col() Say ;
'How many ? (2 to ' + Str(MaxElements, 3, 0) + ') ? '
@ Row(), Col() Get n
Read
QOut()
IF (n < MaxElements); EXIT; ENDIF
END
FOR i := 1 to n
a[ i ] := Random( Seconds() + i )
Qout( a[ i ] )
NEXT i
QOut(); QOut()
Sort( a, n )
FOR i := 1 to n
Qout( a[ i ] )
NEXT i
QOut()
// END.
FUNCTION Random( nSeed )
RETURN INT((((( nSeed * 31415821 ) + 1 );
% 1000000 ) / 1000000 ) * 32767 )
This time I tried to keep it reasonably close to Pascal and if I thought it was important I could make it look even more like Pascal through the use of Clipper's preprocessor directives. Again however. I'm not trying to turn you into a Pascal programmer but rather pointing out that binary sorting algorithms can honestly be termed "ancient" and that one cannot "invent" a binary sort but can only resurrect it.
It works in Pascal, it works in Clipper, what a shock!
A terrific example using Z-80 assembler...
I really wanted to reach into the past for an example and I hope that Zilog appreciates my mention of their classic CPU. Proving that one just doesn't know where the next algorithm will turn up.
This one is from a book called Z80 Assembly Language Programming.
Z80 Assembly Language Programming by Lance A. Leventhal 1979 Osborne/McGraw-Hill (ISBN 0-931988-21-7)
; led.asm
LD A,00001111B ;MAKE PORT B OUTPUT
OUT (PIOCRB),A
LD B,BLANK ;GET BLANK CODE
LD A,(40h) ;GET DATA
CP 10 ;IS DATA A DECIMAL DIGIT?
JR NC,DSPLY ;NO, DISPLAY BLANKS
LD DE,SSEG ;GET BASE ADDRESS OF 7-SEGMENT TABLE
LD H,0 ;MAKE DATA INTO A 16-BIT INDEX
LD L,A
ADD HL,DE ;ACCESS ELEMENT IN TABLE
LD B,(HL) ;GET 7-SEGMENT CODE
DSPLAY: LD A,B
OUT (PIODRB),A
HALT
ORG 20H
SSEG: DEFB 3FH
DEFB 06H
DEFB 5BH
DEFB 4FH
DEFB 66H
DEFB 6DH
DEFB 7DH
DEFB 07H
DEFB 7FH
DEFB 6FH
BLANK: DEFB 00H
OK, I'm willing to give in a little here and admit that you might need a little assembler experience to read this code but you will probably be surprised how much you can get out of it just by reading slowly.
LD A,00001111B Load (LD) the accumulator (A) with 15 binary (00001111B) CP 10 Compare (CP) the accumulator (it's implied) with 10 decimal (10) OUT (PIODRB),A Output (OUT) to address (PIODRB), the accumulator (A) DEFB 06H Define Byte (DEFB) at this location the value 6 hex (06H)
The translation also requires some "wrapper" code since the Z-80 sample provided is the heart of the operation and was not intended to run "as is".
/* led.prg */
#define LED_H REPL( CHR(196), 5 )
#define LED_V CHR(179)
#define BLANK 11
STATIC aSeg := { 63, 6, 91, 79, 102, 109, 125, 7, 127, 103, 0 }
PROCEDURE Led
LOCAL xCursor := SET( _SET_CURSOR, 0 )
LOCAL nKey, cKey
LOCAL aLed := LedNew( 10, 10, { "+W", "+N" } )
LOCAL nCode := aSeg[ BLANK ]
SCROLL()
PIODRB( aLed, nCode ) // DISPLAY BLANK
@ 1, 0 SAY "Press keys 0 - 9 or ESC to exit"
WHILE (( nKey := INKEY( 0 )) != 27 ) // GET DATA
IF IsDigit( cKey := CHR( nKey )) // IS DATA A DECIMAL DIGIT?
nCode := aSeg[ VAL( cKey ) + 1 ] // GET 7-SEGMENT CODE
PIODRB( aLed, nCode ) // DISPLAY
ENDIF
END
SET( _SET_CURSOR, xCursor)
RETURN
FUNCTION LedNew( nRow, nCol, aColor )
RETURN {{ nRow , nCol + 1, LED_H },;
{ nRow + 1, nCol + 6, LED_V },;
{ nRow + 3, nCol + 6, LED_V },;
{ nRow + 4, nCol + 1, LED_H },;
{ nRow + 3, nCol , LED_V },;
{ nRow + 1, nCol , LED_V },;
{ nRow + 2, nCol + 1, LED_H },;
aColor }
PROCEDURE PIODRB( aLed, nCode )
LOCAL nSeg
FOR nSeg := 1 TO 7
@ aLed[ nSeg, 1 ], aLed[ nSeg, 2 ] ;
SAY aLed[ nSeg, 3 ] ;
COLOR aLed[ 8, BitSet( nCode, nSeg - 1 ) ]
NEXT
RETURN
FUNCTION BitSet( nByte, nBit )
RETURN IF( INT((( nByte * ;
( 2 ^ ( 7 - nBit ))) % 256 ) / 128 ) == 1, 1, 2 )
Perhaps an explanation is in order.
The original Z80 code is addressing a parallel input/output circuit (PIO) in the computer to which would be connected a seven-segment LED. The coding scheme used to represent the segments is fixed by the hardware and requires a call to the control register (PIOCR) and the data register (PIODR). There are two of them so they end up named things like PIOCRA and PIODRB.
If this wasn't a great explanation (and I'm sure it wasn't) you'll have to talk to Mr. Leventhal. I got lost somewhere around the words "parallel input/output circuit".
In short, the segments look like this:
.-- a --.
f b
.-- g --.
e c
.-- d --.
Don't judge a book by its subject matter...
Z80 Assembly Language Programming is one example of a book that is considerably more than what the title suggests. It is packed full of stuff.
Chapter 1, "Introduction to Assembly Language Programming" and the chapter on "Assemblers" along with a reference guide to the Z80 instruction set would be expected in such a book. The sample programs are a bonus and the final few chapters are a well thought out treatise on, "The Tasks of Software Development".
Mr. Leventhal, discusses the stages of software development
Do these things sound familiar? It seems that designing applications in Z80 assembler in 1979 is similar to designing applications in Clipper in 1995.
Among the things this book taught me was how an LED operates. If I hadn't seen the Z-80 example I surely wouldn't have written a simulator quite this way. The trick is encoding the LED digits into single bytes by working at the bit level. This lets us represent the entire set in just 10 bytes.
A similar solution could have been managed using 7 bytes per digit (70 bytes total) or even 5 strings per digit for a total of 350 bytes for the set but clearly neither of those solutions would be optimal.
Wrapping up the past and entering the present...
I've encountered dozens of routines that could prove useful to the Clipper developer. In some cases (when written in C or ASM,) the code can be conformed to the rules imposed by Clipper's extend system and simply linked in. In other cases (as I hope you you've seen,) they can be translated into Clipper.
Check out books by Niklaus Wirth and Donald E. Knuth. Books that present code on UNIX tools and general computer science books on stacks, queues, linked-lists and b-trees are filled with great information. Do you work on a network? Many of the Novell network services are directly available to Clipper developers if people would only be willing to take a slightly indirect route to get them.
And one for fun...
Some months back as I was preparing for my sessions at the Netherland's Clipper Devcon I was browsing newsgroups on the Internet. I happened to be skimming some articles in one of the graphic-related newsgroups and stumbled upon a message from a gentleman in Montreal who wrote:
"I'm trying to do a 'bee swarm' effect with no luck. If someone has already made it or has some pseudo code to do it, I'd appreciate a copy or any help on doing this effect. (Preferably using Borland C 3.1 under DOS using mode 13h, but ANY help would be GREATLY appreciated!)"
A reply from Frank Compagner, (fc@butler.fee.uva.nl) at the University of Amsterdam was followed by Pascal code (with imbedded assembler) he had written to demonstrate a swarm effect. That original code is reproduced here as a point of reference.
{written by Frank Compagner, (fc@butler.fee.uva.nl) }
USES Dos,Crt;
CONST
VGA = $A000;
n = 100;
maxq = 4;
maxb = 2;
TYPE
beetype = RECORD
x,y : real;
sx,sy : real;
END;
VAR
bee1,bee0 : ARRAY[0..n] OF beetype;
FUNCTION GetKey : byte;
VAR
k:byte;
BEGIN
ASM
mov ah,6
mov dl,$FF
int $21
mov k,al
END;
GetKey:=k;
END;
PROCEDURE WaitRetrace; ASSEMBLER;
LABEL
l1,l2;
ASM
mov dx,3DAh
l1: in al,dx
and al,08h
jnz l1
l2: in al,dx
and al,08h
jz l2
END;
PROCEDURE Setup;
VAR
i : integer;
BEGIN
ASM
mov ax,0013h
int 10h
END;
Randomize;
FOR i:=0 TO n DO
WITH bee0[i] DO
BEGIN
x:=280*Random+20;
y:=160*Random+20;
sx:=2*Random-1;
sy:=2*Random-1;
END;
END;
PROCEDURE Move;
VAR
i : integer;
dx,dy,d : real;
BEGIN
FOR i:=0 TO n DO
bee1[i]:=bee0[i];
WITH bee0[0] DO
BEGIN
sx:=sx+Random-0.5;
IF sx>maxq THEN
sx:=maxq;
IF sx<-maxq THEN
sx:=-maxq;
sy:=sy+Random-0.5;
IF sy>maxq THEN
sy:=maxq;
IF sy<-maxq THEN
sy:=-maxq;
x:=x+sx;
y:=y+sy;
IF (x<20) OR (x>299) THEN
BEGIN
sx:=-sx;
x:=x+2*sx;
END;
IF (y<20) OR (y>179) THEN
BEGIN
sy:=-sy;
y:=y+2*sy;
END;
END;
FOR i:=1 TO n DO
WITH bee0[i] DO
BEGIN
dx:=bee0[0].x-x;
dy:=bee0[0].y-y;
d:=Sqr(dx)+Sqr(dy);
sx:=sx+8*dx/d+Random-0.5;
sy:=sy+8*dy/d+Random-0.5;
IF sx>maxb THEN
sx:=maxb;
IF sx<-maxb THEN
sx:=-maxb;
IF sy>maxb THEN
sy:=maxb;
IF sy<-maxb THEN
sy:=-maxb;
x:=x+sx;
y:=y+sy;
IF (x<0) OR (x>319) THEN
BEGIN
sx:=-sx;
x:=x+2*sx;
END;
IF (y<0) OR (y>199) THEN
BEGIN
sy:=-sy;
y:=y+2*sy;
END;
END;
END;
PROCEDURE Display;
VAR
i : integer;
BEGIN
WaitRetrace;
FOR i:=0 TO n DO
WITH bee1[i] DO
Mem[vga:320*((200+Round(y)) MOD 200)+(320+Round(x)) MOD 320]:=0;
FOR i:=1 TO n DO
WITH bee0[i] DO
Mem[vga:320*((200+Round(y)) MOD 200)+(320+Round(x)) MOD 320]:=10;
WITH bee0[0] DO
Mem[vga:320*((200+Round(y)) MOD 200)+(320+Round(x)) MOD 320]:=12;
END;
BEGIN
SetUp;
REPEAT
Move;
Display;
UNTIL GetKey=27;
ASM
mov ax,0003h
int 10h
END;
END.
Since I had been telling people how easily converting routines from other languages to Clipper is, I thought I'd take up the challenge with this one.
Targeting it for Clipper 5.2 I knew I would need a routine to set the video graphics mode and to plot a pixel and the easiest way to accomplish that is by using a function found in the Nanforum library. FT_INT86() was written by Ted Means and permits us to directly call DOS interrupts. I've used it to call the DOS video interrupt. The entire library is available free of charge from many places including CompuServe and the Internet but I've included the components needed to link BEES, on the disk.
I began by creating as direct a translation as I could and after the bees were indeed swarming I took on the task of optimizing the little buzzers which leaves us with the following:
/* bees.prg - by Tom Leylan
compile: CLIPPER bees /m/n/w/l
link: BLINKER FI bees, cint86, aint86
*/
#include "ftint86.ch"
#include "inkey.ch"
#define SETPIXEL 12
#define GETMODE 15
#define VID_INT 16
#define GR_MODE 13
#define BEE_0 10
#define BEE_1 12
#define MAXN 30 // number of bees
#define MAXQ 4
#define MAXB 2
#define PX 1
#define PY 2
#define SX 3
#define SY 4
#define SQR(x) ((x)*(x))
STATIC nRnd
#translate Rnd() => ;
(( nRnd := (( nRnd * 31415621 + 1 ) % 100000000 )) / 100000000 )
STATIC aBees[ MAXN ]
FUNCTION Main()
LOCAL xMode := SetMode( GR_MODE )
nRnd := SECONDS()
AEVAL( aBees,;
{| uVal, nEle | aBees[ nEle ] :=;
{ (( 280 * Rnd() ) + 20 ), (( 160 * Rnd() ) + 20 ),;
(( 2 * Rnd() ) - 1 ), (( 2 * Rnd() ) - 1 ) }} )
Plot()
SetMode( xMode )
RETURN 0
PROCEDURE Plot
STATIC aRegs[ INT86_MAX_REGS ]
LOCAL i, dx, dy, d
aRegs[ BX ] := 0 // BH = page number
WHILE !( INKEY() == K_ESC )
aRegs[ CX ] := aBees[ 1, PX ] // CX = column
aRegs[ DX ] := aBees[ 1, PY ] // DX = row
aBees[ 1, SX ] += (( Rnd() - 0.5 ))
aBees[ 1, SX ] :=;
MAX(( aBees[ 1, SX ] := MIN( aBees[ 1, SX ], MAXQ )), -MAXQ )
aBees[ 1, PX ] += aBees[ 1, SX ]
aBees[ 1, SY ] += (( Rnd() - 0.5 ))
aBees[ 1, SY ] :=;
MAX(( aBees[ 1, SY ] := MIN( aBees[ 1, SY ], MAXQ )), -MAXQ )
aBees[ 1, PY ] += aBees[ 1, SY ]
IF (( aBees[ 1, PX ] < 20 ) .OR. ( aBees[ 1, PX ] > 299 ))
aBees[ 1, SX ] *= -1
aBees[ 1, PX ] += ( 2 * aBees[ 1, SX ] )
ENDIF
IF (( aBees[ 1, PY ] < 20 ) .OR. ( aBees[ 1, PY ] > 179 ))
aBees[ 1, SY ] *= -1
aBees[ 1, PY ] += ( 2 * aBees[ 1, SY ] )
ENDIF
aRegs[ AX ] := ( MAKEHI( SETPIXEL ))
FT_INT86( VID_INT, aRegs )
aRegs[ AX ] := ( MAKEHI( SETPIXEL ) + BEE_1 )
aRegs[ CX ] := aBees[ 1, PX ] // CX = column
aRegs[ DX ] := aBees[ 1, PY ] // DX = row
FT_INT86( VID_INT, aRegs )
FOR i := 2 TO MAXN
aRegs[ CX ] := aBees[ i, PX ] // CX = column
aRegs[ DX ] := aBees[ i, PY ] // DX = row
dx := aBees[ 1, PX ] - aBees[ i, PX ]
dy := aBees[ 1, PY ] - aBees[ i, PY ]
d := SQR( dx ) + SQR( dy )
aBees[ i, SX ] += ( ((( 8 * dx ) / d ) + Rnd() ) - 0.5 )
aBees[ i, SX ] :=;
MAX(( aBees[ i, SX ] := MIN( aBees[ i, SX ], MAXB )), -MAXB )
aBees[ i, PX ] += aBees[ i, SX ]
aBees[ i, SY ] += ( ((( 8 * dy ) / d ) + Rnd() ) - 0.5 )
aBees[ i, SY ] :=;
MAX(( aBees[ i, SY ] := MIN( aBees[ i, SY ], MAXB )), -MAXB )
aBees[ i, PY ] += aBees[ i, SY ]
IF (( aBees[ i, PX ] < 0 ) .OR. ( aBees[ i, PX ] > 319 ))
aBees[ i, SX ] *= -1
aBees[ i, PX ] += ( 2 * aBees[ i, SX ] )
ENDIF
IF (( aBees[ i, PY ] < 0 ) .OR. ( aBees[ i, PY ] > 199 ))
aBees[ i, SY ] *= -1
aBees[ i, PY ] += ( 2 * aBees[ i, SY ] )
ENDIF
aRegs[ AX ] := ( MAKEHI( SETPIXEL ))
FT_INT86( VID_INT, aRegs )
aRegs[ AX ] := ( MAKEHI( SETPIXEL ) + BEE_0 )
aRegs[ CX ] := aBees[ i, PX ] // CX = column
aRegs[ DX ] := aBees[ i, PY ] // DX = row
FT_INT86( VID_INT, aRegs )
NEXT
END
RETURN
FUNCTION SetMode( nMode )
LOCAL aRegs[ INT86_MAX_REGS ]
LOCAL nRet
aRegs[ AX ] := MAKEHI( GETMODE )
FT_INT86( VID_INT, aRegs )
nRet := LOWBYTE( aRegs[ AX ] )
aRegs[ AX ] := nMode
FT_INT86( VID_INT, aRegs )
RETURN nRet
The compiled Pascal example is included on the disk and when you run it you will notice a considerable difference in execution speed. Native code compiling strikes again. I've also limited the bee count to 30 rather than use the 100 bees defined in the Pascal source. All in all however, I'm just proving that it works and not suggesting you write real-time, graphical simulations in Clipper.
Keep an eye toward the future...
We can learn a lot from the past but we have to think about and also plan for the future and the future in this case is OOPS. Object-oriented programming is here to stay as is MS Windows or generically speaking, graphical, windowing, operating systems.
The time when DOS-based, text applications could "wow" the audience has passed. No doubt these apps still work and will work for the next couple of years but the advantages to the client, the designer and the developer of multi-tasking, drag and drop, cut and paste and device independence are too great to be ignored.
My advice is don't wait. Don't wait for Computer Associates or any other product vendor to lead the parade. Grab books on related subjects and start studying them now. Then, when the time to choose new tools arrives, you can make an informed decision. Hopefully, now that you've seen how interchangeable programming concepts are, a decision based, not upon some similarity to Clipper but rather the actual features of the product or language.
Remember we're programmers first. We are not C, Pascal, BASIC or in this case Clipper programmers, we are programmers who use C, Pascal, BASIC or in this case, Clipper.
About the author:
Tom Leylan has been a guest speaker at conferences around the world including events in Amsterdam, London, Sydney, Johannesburg, Miami Beach, Orlando, Los Angeles, Palm Desert and Honolulu. He has written numerous articles and is the author of the book, Writing Applications with Clipper © 1994 MIS Press ISBN: 1-55851-382-5.
Tom can be reached at: postmaster@leylan.com
© 1995, The Leylan Factor (All Rights Reserved)