The magazine of the Melbourne PC User Group

Xbase Programming - Part 3
Gary Taig
garyt@melbpc.org.au

[ Continued from last month's article.]
It's back to basics this month. If you found last month's lesson easy to carry out but hard to comprehend, then you should enjoy this one.

Did everyone finish up with a screen that looks like Figure 1? I hope so because Figure 1 was captured from dBASE III PLUS as I tested the code. That screen is exactly as you should have it. This month we will thoroughly dissect the program code behind Figure 1.

If we have space we will write a few lines to enable data entry to the MEMO fields, enabling you to enter some notes for each person. Then we can learn how to MODIFY the data held in the MEMO fields.

Later, in a future lesson, we will work together to develop a more acceptable data entry screen layout 'Me screen in Figure 1 has quite a lot squashed into a small space and, given some time, we can devise far better layouts. I'll show you how to arrange it so that some of your data can be entered onto a second and perhaps even a third screen.

Before We Go Any Further

This month we will write small snippets of code. Check out the built-in text editor your Xbase software provides. In dBASE and Foxbase this is opened by typing
MODIFY  COMMAND at the DOT prompt. As with all Xbase commands, you need only type the first four characters; thus to create a small program named BIGTEST.PRG you need only type MODI COM BIGTEST. Play around with it, learn how it works. Using the built-in editor is much easier and faster for small jobs than jumping out to start another word processor just for small changes.

Are you excited? This is where we move into forward gear. This is where we actually learn how to accomplish something with our Xbase software.

Select Low Gear

If you found the program code in Lesson 2 just a little bit too complex and hard to understand as you read through it, then you should probably slow down. You might read things a wee bit too fast. You could be a bit like me, I do it all the time because I'm always in too much of a hurry. 

You must think about every line. Stop after every line and ask yourself "what does that mean?" or "what does that do?" Sometimes the question might be "what on earth is he saying?!" 

Make notes, that's an important part of this learning exercise. If you really cannot work out what is happening, write down a question. Later it might be answered in the text but at least you won't forget it.

A Tip! 

You might never think of this but it's extremely important. Every now and then you should go back and read the previous lessons. After you have gone through this month's lesson would be an ideal time to revise Lesson 1. You should also look in your manual and check for additional information on the various topics we cover. The reasons are fairly obvious aren't they!

Memory Variables

This is a good time for a brief discussion, just for those who are not familiar with programming.

Memory Variable (a name indicating change), is an expression used to refer to part of the computer's memory that is set aside by your software for the purpose of temporarily storing data.

This memory area gets a special name (we select the name) and it has an address so that it can be accessed. The actual address in memory does not concern us, our ?base software takes care of that. We simply nominate the amount of memory space we require and from time to time. enter some data into it.

What is an Xbase Program?

Some beginners are a little confused by one of the most fundamental aspects of all this. The question of "what is an Xbase program?"

If you're entirely happy with the definition that its like any other computer program, "a series of instructions written in an Xbase language, to be executed by the computer, one at a time, in sequential order", then go back to the text. If that doesn't satisfy you, try carrying out the following exercise at the DOT PROMPT.

Type in each of the following commands and press the [enter] key after each one. Remember to start dBASE or FoxBase or whichever Xbase System you are using before you do this. Typing Xbase instructions at the DOS C:\ prompt will give a variety of unwanted results including "Syntax Error" and "Bad Command or File Name" messages... and it's not recommended because with enough bad luck you could also do some damage.
SET ALTERNATE TO STATUS.DOC 
SET ALTERNATE ON 
DIR 
LIST MEMO 
LIST STATUS 
SET ALTERNATE OFF 
CLOSE ALTERNATE 
CLEAR

You have just carried out manually, eight (8) separate steps, from the keyboard. You have issued eight separate instructions to your software but you did them interactively, remember that from Lesson 1?

QUIT from Xbase and use your favourite file viewer to examine the file named STATUS.DOC that we just created. You will see a variety of information dependent upon how many .DBF files were present in your current directory, how many memory variables were existing in memory at the time and so forth.

Now use your TEXT EDITOR to create a file named TEST.PRG which contains all those instructions, exactly as they are above, on separate lines. Save the file, start your abase System, type DO TEST at the DOT PROMPT and press [enter].

You should also go back to Lesson 1 and find how to avoid data scrolling up the screen. Add the required two (2) lines of code and repeat the exercise.

You have just created a computer program, and ran it. The beauty of a program is that you can run it over and over without the need to type the same old instructions every single time.

We can also go a level higher than that. Can you imagine what the following program code might do...?

FOR i = 1 to 30 
   DO TEST 
ENDFOR

Yes... you would have just saved yourself typing DO TEST 30 times! Think of the total saving in keystrokes in just that little exercise. The benefit of writing a program for repeated tasks becomes pretty obvious, doesn't it.

Note that the FOR ... ENDFOR structure is NOT available in dBASE or Foxhase. However, if you use Foxpro then those commands would be available to you.

As a final exercise, start Xbase, type the command STORE "many digits" TO MVAR and press (enter). Now DO TEST again. Examine the file and you will see a different result. What have you learnt from that?

Another quick one, before we go back to the text. If you leave the filename STATUS.DOC unchanged, that file will be overwritten each time you nun TEST.PRG. I'll show you shortly in another lesson how to create NEW FILENAMES on the run, automatically.

In our sample program you found things like STORE space(10) to mvar. We were telling Xbase to set aside sufficient memory to hold 10 characters, to store initially some ASCII 32 characters (blank spaces), and to use only that memory location whenever we refer to the variable named mvar in our program.


Figure 1.


The exact opposite of a memory reservation such as store, is
RELEASE. When we are done with our variables we can free up that memory space by issuing the command RELEASE mvar.

We are not talking about disk space, that's something entirely different. We are talking about electronic memory - temporary storage! I lave you studied "Eight bits of Impact"? If not, go back to Lesson 1.

There should be several questions on our sample code so let's begin at the top and work down.

Setting Up The Environment

We skipped over thus and agreed to cover it this month. Let's take each line as it comes.

SET TALK OFF

This thing known as TALK, is something is something like ECHO. If you look at the screen in Figure 7 you will notice that I had typed the command line
STORE "many digits" TO mvar, then immediately under that, ran the next line, appears the character string "many digits". That line was echoed to the screen when I hit the ENTER key because TALK was SET ON.

Try it yourself. SET TALK ON and then OFF, type an identical command each time. You will see immediately why we have it SET OFF. Most Xbase activities are echoed to the screen if we leave TALK set on, and that has a tendency to make a real mess of our screens. Try running your program with the SET TALK OFF program line commented out.

Did you understand that? Did you follow exactly what I meant by the words "commented out"? You cannot afford to let one sentence pass by without understanding its meaning or noting a question. When I suggest you try something there is a good reason for it and I'll harp on that until I'm positive you understand the importance of it. You must get into the right habits because in a few months time when we reach some of the complex bits you will need all those good habits to be well and truly SET ON! They must be very much a part of your learning process. Breeze over nothing!

A comment line in an Xbase program is one which begins with an asterisk. You recall that from Lesson 1 didn't you! Remember we put comments in our programs to make them more readable? Well, we temporarily change a program line into a comment line, just by putting an asterisk in front of it. That stops our Xbase software from acting on it. The line is ignored Saves re-typing when we might want to re-use it later!

So, back to where we left off. Comment out the SET TALK OFF and run your program again. See what a mess it makes. Then do the same with SET BELL OFF. The sound effects will teach you more than I could explain in three pages. SET BELL ON can be used quite deliberately when you want to draw the operators attention to what they are doing - like waking them up just in case they are nodding off' after a heavy lunch. Where would you deliberately use SET BELL ON? Think about it. Listen for the BELLS in other programs you use. Try to work out when and why the bell is utilised.

On the other hand, if you just want the bell to ring once at a certain spot, simply add the following line into your program:   
?? CHR(7)

ASCII Character 7 is the BELL character and if you output that to your console (screen) with the single or double question mark "output" command, the bell will ring. Your screen will not display the bullet symbol though, as you might imagine it would.

As a further demonstration of this, key in the following program and run it, just to see what happens. Remember, each line on a new line and don't forget to save it with a .PRG file extension. The first line starts with a single question mark and all others start with two question marks. This will teach you more than a thousand words could ever explain. Watch the screen carefully as your program runs.

? CHR(66) 
?? CHR(101) 
?? CHR(108) 
?? CHR(108) 
?? CHR(7) 
?? CHR(32) 
?? CHR(104) 
?? CHR(97) 
?? CHR(115) 
?? CHR(32) 
?? CHR(114)
?? CHR(117) 
?? CHR(110) 
?? CHR(103)

Now that you have seen the results, try writing that little example in just one line. If you can, and it works, you are doing well. I'll show you later what it should have been.

SET SAFETY OFF

If you have safety SET OFF, that allows you to issue some very dangerous commands without your Xbase Software interrupting and asking "are you sure you want to do that?"

SAFETY always defaults to being SET ON. We must deliberately and consciously SET it OFF when required - Why? Because the software designers knew for certain that some people would cause very serious damage, quite inadvertently, if they were not asked to confirm their intentions in dangerous situations. The designers decided it was better to be safe than sorry because we all lose concentration at times.

For instance, if you type the command ZAP and then {enter) when you have a .DBF file open, the file contents can be lost for ever. ZAP is the command that permanently DELETES or if you like REMOVES, all records in a file. Setting SAFETY OFF allows you to do that and get away with it, without any warning. So use it carefully, only after you have tested your programs absolutely thoroughly.

When I began using dBASE, I used to have SAFETY set ON at all times, and I should have left it on too because now I drink too much coffee. dBASE used to display the message "are you sure you need that..?" and ring the bell, every time I went to move away from the keyboard and head for the kitchen! Come on - lighten up a bit - life is too short! That was just one of my versions of SET ALERT ON.

SET DATE is self explanatory. The choices are in your manual. Check them out.

SET STATUS shows or hides the status bar. One lesson you will learn by using it, is that when you SET STATUS OFF or ON in the middle of a program, the screen is cleared and you must repaint the entire screen immediately. Sometimes it's handy, other times it's a nuisance. Later I'll show you the best way to get around that one.

Inexplicable Events

That reminds me of another small problem. We had better diverge here for a moment and cover a very important aspect of these Xbase Systems. They all have their peculiarities. Such as SET STATUS clearing the screen for us, without invitation.

I have seen these referred to as bugs. Some people think if a System behaves in a certain way not to their liking, or in a way they perceive is incorrect, then that constitutes a bug in the Software.

I have seen them referred to as features. Usually though it's the Software Developers who call them features, with big tongue in cheek.

They are also known as quirks, aberrations, irregularities and many other names but really they fall into either one of two categories. Either it is something the designers missed entirely, and found when it was far too late for them to make the change. Or, it was something they couldn't change, because it would have meant changing something else as well, and they didn't quite want to do that.

If you use FoxBase 2.1 you will find that the screen clears every time you SET SCOREBOARD OFF. There are some undocumented things like that you will discover from time to time. Finding a way around them helps you to learn by forcing you to do some lateral , so stay with it

Back to our Program

CLEAR simply clears the screen, like the CLS command in DOS or Basic. It's a good habit and more often than not you'll find it necessary. By that I mean, "experience will teach you" it's a wise habit to clear the screen at the beginning of every program.

SET PROCEDURE TO <program-name>

We've already briefly covered SET PROCEDURE TO, in Lesson 2. I'll tell you a lot more about it later when we get to the point where you will be better equipped to understand and follow a more detailed discussion.

For now, just remember the following two important items and the explanation that comes after it:

(1) When you issue a command
DO (program-name) your Xbase Software runs around looking for a program of that name, which it can execute. A program usually has the .PRG file extension.

(2) Any Xbase program, that is, a program code file with the .PRG file extension, can also be a Procedure File. The simple distinction is that it contains Procedures.

If you have provided a procedure file name to your current session, by issuing the command 
SET PROCEDURE TO <program-name> 
either manually at the DOT prompt, or from within a program, this causes your Xbase Software to look through that file for every program it is required to execute.

Yes, that's right. From then on, every time you issue the command
DO <some-other-program-name> 
in the current session, Xbase will search entirely through the Procedure File first. It will be looking for a line starting with
PROCEDURE <some-other-program-name>.

Only if the required line is not present, will Xbase go off hunting around on your disk trying to find a separate program to execute.

Make sure you understand what this also means. If a file contains a procedure with the same name as another, separate program, and you have already SET PROCEDURE TO that file, the procedure will be found and executed, not the other separate program.

In Lesson 2 our program file was named MBINPUT.PRG and it contained procedures - MAIN, BLNKVARS, and PITITIN. To really see this principle demonstrated, split a copy of MB INPUT.PRG into four separate programs named MBINPUT.PRG, MAIN.PRG, BLNKVARS.PRG and PUTITIN.PRG. Comment out the line that says SET PROCEDURE TO MBINPUT. comment out each of the PROCEDURE lines if you still have them, then try running MBINPUT.PRG again.

Everything will work perfectly, the only difference now is that your system is not so efficient, not that it makes much difference here of course, but Xbase now must search on your disk to find the other programs each time they are called. (Refer to Figure 2 for a brief discussion on the speed of a hard disk vs the speed of memory.)
Nanoseconds, Milliseconds and Disk Access

The subject of "Disk Access" time prompts me to make the following comparison. This will be somewhat simplistic but it will serve to highlight the huge difference between the speed of your computer's memory and the time it takes your machine to "find" the start of a file on a hard disk.

Memory Chips

The average XT uses memory chips with a rated speed of somewhere between 100 and 150 nanoseconds. In contrast, if you have a late model 286 or 386, running at 16 or 20 MHz, your memory is more likely to be rated in the 70 to 80 nanosecond category.

Disk Access

Obviously disk speed varies with the type of disk. If you use an older disk with an average rated seek time of 50 to 70 milliseconds, that's fairly slow by present day standards. If you have a new disk with an average seek time of (say) 12 to 14 milliseconds, your disk is comparatively fast.

One millisecond is 1/1000th of a second. That's a short period of time, isn't it!

A slow old disk can perform an average seek in just 70/1000ths of a second - or in other words, in terms of rated speed, complete about 14 such activities, every second.

Some Big Numbers

One second is equal to 1,000 milliseconds One millisecond equals 1,000 microseconds One microsecond equals 1,000 nanoseconds

If we reach for the calculator, or exercise some grey matter, we find there are 1,000 million nanoseconds in every second.

Oranges and Apples

One disk access, which takes 70 million nanoseconds, is competing with your memory which has a rated speed, for a vaguely similar exercise, of as low as 70 nanoseconds. Now that does not make your memory one million times faster than your disk because we are comparing apples
with oranges but it does mean that your memory can perform approximately one million address operations in the time it takes your disk to perform one average seek.

If you machine is running at 20 MHz, then it is capable of doing almost 20 million single program instructions per second. Compare that massive amount of work, with the 14 miserable "seek" activities your slow disk might undertake in the same period of time.

If you must make a programming decision in favour of either extra memory activity or extra disk activity, choose memory. You will get a lot of work done while that disk finds the required file and retrieves the data you need. Data that in some cases you might have had, and thrown away quite needlessly, a few millionths of a second earlier.

Figure 2

PROCEDURE MAIN

You'll remember that I ran out of room last month and was forced to cut some code from the sample so it would fit? Well, just to make sure you managed to get it right I've included those few lines for you in full this month. Compare Figure 3 with the code you wrote on your own. Mark yourself very strictly though, because it must be exactly correct.


Figure 3

Memory Variables - Public vs Private

I'm sure many of you would like an explanation of this. I wrote Lesson 2 in a bit of a hurry and couldn't possibly handle it there so here we go. Variables can be either PUBLIC or PRIVATE. 

Some of what you learn here changes in later versions of some Xbase Systems but the fundamentals are still important and we'll start our coverage of PUBLIC vs PRIVATE from the way they were in the beginning - that is, back in the days of dBASE III and FoxBase. 

To understand this properly we must firstly examine how our program code is executed. We must look closely at the order of processing of the instructions in our program file.

Take a look at Figure 4. Each PROCEDURE is represented by a separate box. (Remember, these are known in some circles as SUB-ROUTINES). The first Box is not a procedure though, it is simply the start of the program. In fact, it would represent the entire content of MBINPUT.PRG if you split the three procedures out into separate programs as we discussed earlier.
MBINPUT.PRG calls the procedure; named MAIN. This in turn calls BLNKVARS and PUTITIN.

We could not allow the memory variables that we create in MAIN.PRG to be created at the lower level in BLNKVARS instead, because when processing returned from BLNKVARS to MAIN, any variables created in BLNKVARS would be released. [There is another twist on this but we'll cover it last!]

Last month I told you that a program called by MAIN could not release memory variables that existed at the time it was called. That is not correct if you are working with some of the earlier versions because dBASE III PLUS for instance, is quite happy to do that. The best way to find the limits of your software is to create a small program to test the situation. Follow the exercise below.

(1) Create some variables from the DOT prompt. Do this by typing the commands as they would appear in a program e.g.
STORE "1213" TO char_var 
STORE 15 TO num_var 
STORE "abcdefgh" TO next_var

We created three different variables. The first and third now contain character strings; the second, a numeric value.

(2) Type DISPLAY MEMORY and [enter]. You will see that your memory contains at least those three variables. They will be PUBLIC by default.)

Now, to test our particular Xbase software we need to run some programs. If your software cannot release variables created by another program, you will soon find out. Key in the following program and run it, without leaving Xbase.

STORE "zzzzz" to new_var 
RELEASE char_var,num_var,next_var 
DISP MEMO 
SUSPEND


Figure 4

What do you see? If your memory contents displayed on screen include the three variables we just now tried to release, then you have discovered that your software will not allow a program to release PUBLIC variables created at the DOT prompt. So far so good. Now we had better test that same thing in a program environment.

Write a small program to duplicate the first lot of commands we typed above at the DOT prompt. This will become the calling program, we'll refer to it as the "higher level" program

From within that program you must run the small program that tries to release the variables and see what happens. If the variables are still in memory when the higher level program displays the current memory, then you will know your software does not allow a lower level program to release memory previously reserved by another.

Are you having trouble working out where to call the other program? If yes, this is what your higher level program should do:

(a) Store some information, any data type is OK, into some memory variables, like we did in Step (1) above, at the DOT prompt.

(b) Call your other program, whatever you named it, with the command
DO <YOUR-PROGRAM>. This will try to release the memory reserved.

(c) Next, include the command
DISPLAY MEMORY. This will show you what remains in memory after the lower level program has tried to release the variables created at the higher level.

Are you making notes at the top of these programs? Are you noting what each program does? Are you noting the changes you make? Are you getting the idea that writing programs involves creating an awful lot of documentation? Either you are, or you're not doing the job properly! There is no other way to learn.

Public Variables

The original intention behind declaring a variable PUBLIC, was that any program could use the variable, modify its content etc, but not release it. The variable was PUBLIC property

As it is, dBASE III allowed a lower level program to release memory reserved by another. Common opinion is that dBASE should not have allowed this to happen. Later, more sophisticated Xbase Systems don't allow it.

Private

If a lower level program wants to use an existing variable, perhaps change its content or manipulate it in some other way, but not affect the existing content, that can be done. But, the variable must be declared PRIVATE, within that lower level program, before any messing around takes place.

In some respects PRIVATE is rather like allowing a lower level program to re-use the name already used by another variable. When the existing variable contains some data, the lower level program can also get hold of that data, manipulate it, discard whatever values it finished up with and so on, without affecting the actual value stored in the original variable.

In our sample system, BLNKVARS certainly does alter the contents of all variables. It stores spaces and other blank data to them but in that particular case we wanted the change to be retained. so in that case we would have no need whatsoever to make the variables PRIVATE within BLNKVARS, would we!

Test this by trying to run your program with the variables declared PRIVATE in BLNKVARS, before any blanks are stored in them, and see what happens. You will learn a lot from that exercise. Examine the results by displaying memory before, during and after the execution of BLNKVARS.

Why not Make Variables Public In BLNKVARS

Wouldn't they be available and still existing when we returned? Yes they would!

We could actually create all of our variables and store blank spaces in them, from within PROCEDURE BLNKVARS, make them PUBLIC, and as a result, find them still available when we returned. However, there is a catch.

We would not be able to call PROCEDURE BLNKVARS again during that session, because you cannot declare a variable PUBLIC once it has already been declared PUBLIC.

We would be forced to find a way around that problem. See if you can work it out, there is at least one way around it. I'll show you next lesson. 

By now you should be getting the impression that there is more than one way of achieving each end result. There is - often we could write the same program in many different ways. I hope you practice a lot and play around with ideas as we are doing this because as the inimitable Tom Coleman once put it - "computer knowledge is absorbed through the tips of the fingers".


Figure 5


Figure 6



Data Entry Take a good look at Figures 5 and 6. These explain a little about how our software locates information on the screen. Figure 6 actually explains how you can enter data into memory variables at the DOT prompt. Type the following commands and test it yourself, you will learn much by doing this.

STORE space(10) to mvar 
@ 10, 10 GET mvar 
READ

You will find in this instance that the cursor jumps to the location of the variable and allows you to enter data. Type something! After you press [enter] you will be put back at the DOT prompt. Display your memory at that point and you will see that mvar contains whatever you typed into it.

Do the same again but next time STORE 0 (zero) to a variable. Then GET it, somewhere in the middle of the screen, and issue a READ. You'll find the variable contains numeric data instead of character data. Everything is spelled out for you when you DISP MEMO.

Look in your manual for PICTURE clauses, try adding some PICTURE clauses to the GET and READ for numeric variables.

There is enough in that little exercise to keep you going for a month or more. You will learn heaps by doing these things. Remember, make notes all the time!!

The case structure in our code, at the bottom of PROCEDURE MAIN, simply reads each of the key presses and branches control accordingly. The Readkey() values are all contained in your manual, study them carefully. After a while, with constant use, they become like the "three times tables", you will know them by heart

Reprinted from the December 1991 issue of PC Update, the magazine of Melbourne PC User Group, Australia

 

[About Melbourne PC User Group]